Analisi dell'utilizzo della memoria: Java vs C ++ trascurabile?


9

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

Il motivo per cui l'ho chiesto è perché stavo leggendo l'importanza di sapere quando i requisiti di memoria di un programma impediranno al programmatore di risolvere un determinato problema.

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

  • 4 byte per la sua variabile di istanza int
  • 16 byte di sovraccarico (riferimento alla classe dell'oggetto, informazioni sulla garbage collection e informazioni sulla sincronizzazione)
  • 4 byte di imbottitura

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

  • 24 byte di informazioni sull'intestazione
  • 16 byte di overhead dell'oggetto
  • 4 byte per lunghezza
  • 4 byte per imbottitura
  • oltre alla memoria necessaria per memorizzare i valori

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

Ero ignaro dell'utilizzo della memoria dei programmi C ++ e Java che ho scritto, ma ora che sto iniziando a conoscere gli algoritmi, sto apprezzando maggiormente le risorse del computer.


6
Che cos'è un "oggetto intero" in C ++? int? In tal caso, dovresti confrontarlo con intin Java, non Integer- purché i tuoi C ++ siano 32 bit.
Mat

+1 Se ho creato una classe c ++ che aveva solo una variabile int, l'ho istanziata
Anthony,

3
un int non è un int - dipende dalla piattaforma

1
Un C ++ con un solo membro int non avrà in genere alcun sovraccarico. Utilizzerà esattamente lo stesso spazio utilizzato dalla piattaforma per memorizzare un int (in genere 4 byte sulle piattaforme PC attuali).
Dirk Holsopple,

+1 per un programmatore Java incuriosito dalla memoria. Più di ogni altra cosa, la consapevolezza della memoria è il fattore più importante che determina le prestazioni delle architetture moderne.
imallett,

Risposte:


15

Dipende dalla piattaforma e dall'implementazione.

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

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

L'attuale modello di memoria di C ++ è tuttavia molto compatto e prevedibile . Ad esempio non ci sono metadati in oggetti, matrici o puntatori. Strutture e classi sono contigue come le matrici, ma il riempimento può essere posizionato dove necessario e necessario.

Francamente, tuttavia, tale confronto è sciocco nella migliore delle ipotesi in quanto l'utilizzo della memoria Java dipende più dall'implementazione Java che dal codice che esegue.


1
Vero, ma per motivi di confronto direi che dovremmo assumere tipi interi di dimensioni equivalenti (anche se sono disponibili solo con nomi diversi). Dopotutto, dimensioni diverse significano semantica diversa e su molte (non tutte) piattaforme comuni le dimensioni sono identiche o numeri interi della stessa dimensione sono disponibili con nomi diversi.

Nota per l'OP: è meglio scegliere da soli le dimensioni dell'intero: se si desidera un int a 32 bit in C ++, è possibile utilizzare int32_t.
K.Steff,

9

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

Innanzitutto, in moltissimi Java, praticamente non si vede mai un raw int: quasi tutti gli utilizzi Integer, quindi il fatto che un intpossa avere (circa) le stesse dimensioni di un intin C o C ++ è quasi irrilevante, tranne che ( nella mia esperienza, piccola) percentuale di codice che utilizza solo al intposto 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 riguarda principalmente il modo in cui il garbage collector è stato messo a punto. Nella maggior parte dei casi, il GC è ottimizzato per massimizzare la velocità, il che (in gran parte) significa far funzionare il GC il più raramente possibile.

Al momento non ho un link utile, ma ci sono stati alcuni test che dimostrano che Java può funzionare alla stessa velocità di C, ma per farlo devi eseguire il GC abbastanza raramente da usarlo circa 7 volte di 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ò liberare memoria solo quando può "provare" che non c'è più modo di accedere a un oggetto, piuttosto che semplicemente quando sai che hai finito di usarlo. Ciò significa che anche se si esegue il GC molto più spesso per ridurre al minimo l'utilizzo della memoria, è probabile che si possa comunque pianificare un tipico programma con un footprint di memoria più grande. In un caso come questo, è possibile ridurre il fattore a 2 o 3 anziché a 7. Anche se si va drasticamente in mare, tuttavia, non '1 .

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


1 Questo non vuol dire che nessuno potrebbe riuscire a scrivere Java con un footprint così vicino a quello che otterresti con C ++. E 'solo per dire che basta avere lo stesso numero / dimensioni degli oggetti, e l'esecuzione del GC in realtà spesso non ti arriva come una regola.


7
Per quanto riguarda il tuo primo punto: non sono un ragazzo Java, ma le API Java che ho visto non hanno mai usato Integer(perché dovrebbero?) Invece di int. Solo le raccolte generiche non hanno altra scelta che usarle a Integercausa della cancellazione dei tipi, ma se ti importava potresti sostituirle con un'implementazione specializzata into di qualsiasi tipo primitivo di cui hai bisogno. E poi c'è la boxe temporanea per passare attraverso il codice di wrapping generico (ad esempio tutto ciò che richiede un Object[]). A parte questo, hai fonti per l'overhead dello spazio GC? Non ne dubito davvero, sono solo curioso.


9

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

Oggetti C ++ non lo fanno (in genere) hanno bisogno di alcuna archiviazione, tranne ciò che i membri hanno bisogno. Si noti che (a differenza di Java, dove tutto definito dall'utente è un tipo di riferimento), il codice client può utilizzare un oggetto sia come tipo di valore che come tipo di riferimento, ovvero un oggetto potrebbe memorizzare un puntatore / riferimento a un altro oggetto o archiviare l'oggetto direttamente senza riferimento indiretto. Un puntatore aggiuntivo per oggetto è necessario se ci sono virtualmetodi, ma alcune classi utili sono progettate per andare d'accordo senza polimorfismo e non ne hanno bisogno. Non ci sono metadati GC e nessun blocco per oggetto. Pertanto gli class IntWrapper { int x; public: IntWrapper(int); ... };oggetti non hanno bisogno di più spazio delle semplici inte possono essere posizionati direttamente (cioè senza riferimento indiretto) in raccolte e altri oggetti.

Le matrici sono complicate semplicemente perché non esiste un equivalente comune prefabbricato di una matrice Java in C ++. Potresti semplicemente allocare un gruppo di oggetti con new[](senza assolutamente overhead / metadata) ma non c'è un campo di lunghezza - l'implementazione probabilmente ne memorizza uno ma non puoi accedervi. std::vectorè un array dinamico e quindi ha un sovraccarico aggiuntivo e un'interfaccia più grande. std::arraye array in stile C (int arr[N];), è necessaria una costante di compilazione. In teoria, dovrebbe essere solo l'archiviazione dell'oggetto più un singolo intero per la lunghezza, ma poiché è possibile ottenere il ridimensionamento dinamico e un'interfaccia completa con pochissimo spazio aggiuntivo, in pratica lo si fa. Si noti che tutti questi, così come tutte le altre raccolte, per impostazione predefinita memorizzano gli oggetti in base al valore, risparmiando in tal modo indiretta e spazio per i riferimenti e migliorando il comportamento della cache. È necessario memorizzare esplicitamente i puntatori (quelli intelligenti, per favore) per ottenere l'indirizzamento indiretto.

I confronti di cui sopra non sono del tutto equi, poiché alcuni di questi risparmi sono offerti non includendo le funzionalità incluse in Java e il loro equivalente C ++ è spesso meno ottimizzato dell'equivalente Java (*). Il modo comune di implementare virtualin C ++ impone esattamente tanto overhead quanto il modo comune di implementare virtualin Java. Per ottenere un blocco, hai bisogno di un oggetto mutex con funzionalità complete, che molto probabilmente è più grande di qualche bit. Per ottenere il conteggio dei riferimenti ( noequivalente 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 stato 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 nel modo giusto, il puntatore condiviso può (deve?) Essere ancora due puntatori anziché uno. Inoltre, il buon stile C ++ non utilizza queste funzionalità abbastanza per essere importante: in pratica, gli oggetti di una libreria C ++ ben scritta ne usano meno. Ciò non significa necessariamente un minore utilizzo complessivo della memoria, ma 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 gli oggetti che sono difficilmente avrà bisogno di serrature. Vedi l' 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.


3

Un piano int, in Java, occupa esattamente lo stesso spazio di un intC ++, a condizione che entrambe le implementazioni utilizzino la stessa dimensione di intero e allineamento di memoria.

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

E poi ci sono considerazioni di ottimizzazione; poiché i modelli di esecuzione e i paradigmi di programmazione differiscono, è improbabile che qualsiasi problema non banale venga risolto allo stesso modo in entrambe le lingue, quindi confrontare le dimensioni di archiviazione su questo livello non ha molto senso.

Sì, per impostazione predefinita gli oggetti Java hanno un sovraccarico maggiore rispetto alle classi C ++, ma sono dotati di più funzionalità e questo porta a un diverso stile di programmazione: un buon programmatore può sfruttare gli aspetti positivi e negativi di entrambi i linguaggi.


+1 Più in testa, ma più funzioni in Java, capisco ora, grazie
Anthony
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.