Analisi dell'uso della memoria: Java vs C ++ Trascurabile?

9

In che modo l'utilizzo della memoria di un oggetto intero scritto in Java confronta \ in contrasto con l'utilizzo della memoria di un oggetto intero scritto in C ++? La differenza è trascurabile? Nessuna differenza? Una grande differenza? Sto indovinando che è lo stesso perché un int è un int indipendentemente dalla lingua (?)

Il motivo per cui ho chiesto questo è perché ero leggendo sull'importanza di sapere quando i requisiti di memoria di un programma impedire al programmatore di risolvere un determinato problema.

Ciò che mi ha affascinato è la quantità di memoria richiesta per la creazione di un singolo oggetto Java. Prendiamo ad esempio un oggetto intero. Correggetemi se ho torto ma un oggetto intero Java richiede 24 byte di memoria:

  • 4 byte per la sua variabile istanza int
  • 16 byte di overhead (riferimento alla classe dell'oggetto, informazioni sulla raccolta dei rifiuti e informazioni sulla sincronizzazione)
  • 4 byte di padding

Come altro esempio, un array Java (che è implementato come un oggetto) richiede 48 + byte:

  • 24 byte di informazioni sull'intestazione
  • 16 byte di overhead degli oggetti
  • 4 byte per la lunghezza
  • 4 byte per il riempimento
  • più la memoria necessaria per memorizzare i valori

In che modo questi usi della memoria si confrontano con lo stesso codice scritto in C ++?

Ero ignaro dell'uso della memoria dei programmi C ++ e Java che ho scritto, ma ora che sto iniziando a conoscere gli algoritmi, ho un maggiore apprezzamento per le risorse del computer.

    
posta Anthony 18.08.2012 - 12:19
fonte

4 risposte

15

Dipende dalla piattaforma e dall'implementazione.

C ++ garantisce che la dimensione di char sia esattamente un byte e larga almeno 8 bit. Quindi, la dimensione di short int è almeno di 16 bit e non inferiore a char . La dimensione di un int è almeno pari alla dimensione di short int . La dimensione di long int è almeno di 32 bit e non inferiore a int.

sizeof(char) == 1; sizeof(long int) >= sizeof(int) >= sizeof(short int) >= sizeof(bool) >= sizeof(char).

Il modello di memoria reale del C ++ è tuttavia molto compatto e prevedibile . Ad esempio non ci sono metadati in oggetti, matrici o puntatori. Strutture e classi sono contigue come gli array, ma il padding può essere posizionato dove necessario e necessario.

Francamente, tuttavia, tale confronto è sciocco al meglio, poiché l'utilizzo della memoria Java dipende più dall'implementazione Java che dal codice che esegue.

    
risposta data 18.08.2012 - 12:34
fonte
9

La maggior parte delle risposte sembra ignorare un paio di punti abbastanza importanti.

In primo luogo, in un sacco di Java, praticamente non vedi mai un int grezzo - quasi tutto l'uso è di Integer , quindi il fatto che un int potrebbe essere (circa) della stessa dimensione di un int in C o C ++ è quasi irrilevante, eccetto per quella (nella mia esperienza, piccola) percentuale di codice che utilizza solo int invece di Integer .

In secondo luogo, le dimensioni dei singoli oggetti non hanno quasi nulla a che fare con l'impronta di memoria di un programma nel suo insieme. In Java, l'impronta di memoria del programma si riferisce principalmente al modo in cui il garbage collector è stato ottimizzato. Nella maggior parte dei casi, il GC è ottimizzato per massimizzare la velocità, che (in gran parte) significa eseguire il GC il meno frequentemente possibile.

Non ho un collegamento a portata di mano al momento, ma ci sono stati alcuni test che dimostrano che Java può girare alla stessa velocità di C, ma per farlo devi eseguire il GC abbastanza raramente da usarlo circa 7 volte più memoria. Questo non perché i singoli oggetti sono 7 volte più grandi, ma perché GC può diventare piuttosto costoso se lo fai troppo spesso. Peggio ancora, GC può rilasciare memoria solo quando può "dimostrare" che non c'è più alcun modo di accedere ad un oggetto, piuttosto che semplicemente quando sai di aver finito di usarlo. Ciò significa che anche se esegui il GC molto più spesso per ridurre al minimo l'utilizzo della memoria, probabilmente puoi ancora pianificare un programma tipico con un ingombro di memoria più grande. In un caso come questo, è possibile ridurre il fattore a 2 o 3 anziché a 7. Anche se si passa drasticamente in overboard, tuttavia, non aspettarsi che i due si trovino all'interno, ad esempio, del 10 o 20% dello stesso utilizzo di memoria, anche se il numero e la dimensione degli oggetti che hai assegnato sono identici 1 .

A seconda della situazione, c'è un altro fattore che può o non può essere significativo: la memoria occupata dalla JVM stessa. Questo è più o meno fisso, quindi una percentuale può essere enorme se l'app non ha bisogno di molta memoria propria, o potrebbe essere minuscola se l'app ha bisogno di memorizzare molto. Almeno sulla mia macchina, anche la più banale app Java sembra occupare qualcosa come 20-25 megabyte (potrebbe essere oltre 1000x per i programmi trivlal, o quasi infinitamente piccola per i più grandi).

1 Questo non vuol dire che nessuno possa mai riuscire a scrivere Java con un ingombro simile a quello che si otterrebbe con C ++. È solo per dire che solo avere lo stesso numero / dimensione di oggetti, e l'esecuzione del GC molto spesso non ti porterà lì come regola.

    
risposta data 18.08.2012 - 18:15
fonte
9

Spero che tu ti renda conto che tutto questo è profondamente definito dall'implementazione, sia per Java che per C ++. Detto questo, il modello a oggetti di Java richiede un bel po 'di spazio.

Gli oggetti C ++ non hanno (generalmente) bisogno di qualsiasi storage tranne per quello di cui i membri hanno bisogno. Si noti che (a differenza di Java, dove tutto è definito dall'utente come un tipo di riferimento), il codice client può utilizzare un oggetto sia come tipo di valore che come tipi di riferimento, ovvero un oggetto può memorizzare un puntatore / riferimento a un altro oggetto o archiviare direttamente l'oggetto senza indirezione. Un puntatore aggiuntivo per oggetto è necessario se ci sono metodi virtual , ma alcune classi utili sono progettate per andare avanti senza polimorfismo e non ne hanno bisogno. Non ci sono metadati GC e nessun blocco per oggetto. Pertanto gli oggetti class IntWrapper { int x; public: IntWrapper(int); ... }; non hanno bisogno di più spazio di int s, e possono essere posizionati direttamente (cioè senza riferimento indiretto) in raccolte e altri oggetti.

Gli array sono complicati semplicemente perché non esiste un equivalente comune, preimpostato, di un array Java in C ++. Potresti semplicemente allocare un gruppo di oggetti con new[] (senza alcun overhead / metadata) ma non c'è un campo lunghezza - l'implementazione probabilmente ne memorizza uno ma non puoi accedervi. std::vector è un array dinamico e quindi ha un overhead aggiuntivo e un'interfaccia più grande. std::array e array in stile C ( int arr[N]; ), hanno bisogno di una costante in fase di compilazione. In teoria, dovrebbe essere solo lo spazio di archiviazione dell'oggetto più un singolo intero per la lunghezza, ma dal momento che è possibile ottenere il ridimensionamento dinamico e un'interfaccia completa con pochissimo spazio in più, in pratica basta fare questo. Si noti che tutte queste, insieme a tutte le altre raccolte, hanno come impostazione predefinita la memorizzazione degli oggetti in base al valore, salvando in tal modo il riferimento indiretto e lo spazio per i riferimenti e il miglioramento del comportamento della cache. È necessario archiviare esplicitamente i puntatori (quelli intelligenti, per favore) per ottenere l'indirezione.

I confronti sopra riportati non sono del tutto equi, poiché alcuni di questi risparmi sono garantiti non includendo le funzionalità Java include, e il loro equivalente C ++ è spesso meno ottimizzato rispetto all'equivalente Java (*). Il modo comune per implementare virtual in C ++ impone un sovraccarico tanto quanto il modo comune di implementare virtual in Java. Per ottenere un blocco, è necessario un oggetto mutex con funzioni complete, che è probabilmente più grande di alcuni bit. Per ottenere il conteggio dei riferimenti ( non equivalente a GC e non dovrebbe essere usato come tale, ma a volte utile), è necessario un puntatore intelligente che aggiunge un campo di conteggio dei riferimenti. A meno che l'oggetto non sia costruito con cura, il conteggio dei riferimenti, l'oggetto puntatore intelligente e l'oggetto referenziato si trovano in posizioni completamente separate e, anche quando lo si costruisce correttamente, il puntatore condiviso può essere (necessario?) Due puntatori invece di uno. Inoltre, un buon stile C ++ non usa queste caratteristiche abbastanza per essere importante - in pratica, gli oggetti di una libreria C ++ ben scritti usano meno. Ciò non significa che necessariamente significhi un minor utilizzo di memoria in generale, ma ciò significa che C ++ ha un buon vantaggio in questo senso.

(*) Ad esempio, è possibile ottenere chiamate virtuali, codici hash di identità e blocco con una sola parola per alcuni oggetti (e due parole per molti altri oggetti) unendo le informazioni sul tipo con vari flag e rimuovendo i bit di blocco per oggetti che improbabilmente necessitano di serrature. Vedi Implementazione efficiente in termini di spazio e tempo del modello a oggetti Java (PDF) di David F. Bacon, Stephen J. Fink e David Grove per una spiegazione dettagliata di questa e altre ottimizzazioni.

    
risposta data 18.08.2012 - 13:06
fonte
3

Un semplice int , in java, occupa esattamente lo spazio di int in C ++, a condizione che entrambe le implementazioni utilizzino la stessa dimensione integer e l'allineamento di memoria.

Un int 'oggetto' (un boxed integer, cioè un'istanza di classe Integer ), porta tutto il sovraccarico di un'istanza di classe in Java, quindi è significativamente più grande di un int in C ++. Tuttavia, se dovessi equipaggiare un oggetto in C ++ con le stesse caratteristiche che gli oggetti Java hanno con out-of-the-box (polimorfismo, boxe, garbage collection, RTTI), probabilmente ti ritroverai con un oggetto uguale dimensioni.

E poi ci sono considerazioni sull'ottimizzazione; poiché i modelli di esecuzione e i paradigmi di programmazione differiscono, è improbabile che qualsiasi problema non banale possa essere risolto nello stesso modo in entrambi i linguaggi, quindi confrontare le dimensioni dello spazio di archiviazione a questo livello non ha molto senso.

Sì, gli oggetti Java hanno un sovraccarico per impostazione predefinita rispetto alle classi C ++, ma sono dotati di più funzioni e questo porta a uno stile di programmazione diverso: un buon programmatore può sfruttare gli aspetti positivi e negativi di entrambe le lingue.

    
risposta data 18.08.2012 - 14:13
fonte

Leggi altre domande sui tag