Analyse der Speichernutzung: Java vs C ++ Vernachlässigbar?

9

Wie vergleicht sich die Speichernutzung eines in Java geschriebenen Integer-Objekts mit der Speichernutzung eines in C ++ geschriebenen Integer-Objekts? Ist der Unterschied vernachlässigbar? Kein Unterschied? Ein großer Unterschied? Ich vermute, es ist dasselbe, weil ein int ein int ist, unabhängig von der Sprache (?)

Der Grund, warum ich dies gefragt habe, ist, dass ich gelesen habe, wie wichtig es ist, zu wissen, wann der Speicherbedarf eines Programms den Programmierer daran hindert, ein bestimmtes Problem zu lösen.

Was mich fasziniert hat, ist die Menge an Speicher, die zum Erstellen eines einzelnen Java-Objekts benötigt wird. Nehmen Sie zum Beispiel ein ganzzahliges Objekt. Korrigieren Sie mich, wenn ich falsch liege, aber ein Java-Integer-Objekt 24 Byte Speicher benötigt:

  • 4 Bytes für die int-Instanzvariable
  • 16 Byte Overhead (Verweis auf die Klasse des Objekts, Informationen zur Speicherbereinigung und Informationen zur Synchronisierung)
  • 4 Bytes Auffüllen

Als weiteres Beispiel benötigt ein Java-Array (das als Objekt implementiert ist) mehr als 48 Byte:

  • 24 Byte Header-Informationen
  • 16 Bytes Objekt-Overhead
  • 4 Bytes für die Länge
  • 4 Bytes zum Auffüllen
  • plus den Speicher, der zum Speichern der Werte benötigt wird

Wie vergleichen sich diese Speichernutzungen mit demselben Code, der in C ++ geschrieben wurde?

Früher war ich mir der Speichernutzung der von mir geschriebenen C ++ - und Java-Programme nicht bewusst, aber jetzt, da ich anfange, etwas über Algorithmen zu lernen, schätze ich die Ressourcen des Computers besser.

Anthony
quelle
6
Was ist ein "Integer-Objekt" in C ++? int? Wenn ja, sollten Sie das mit intJava vergleichen, nicht Integer- solange Ihre C ++ - Ints 32 Bit sind.
Mat
+1 Wenn ich eine c ++ - Klasse erstellt habe, die nur eine int-Variable hatte, dann habe ich sie instanziiert
Anthony
3
ein int ist kein int - es ist plattformabhängig
1
Ein C ++ mit nur einem int-Mitglied hat normalerweise keinen Overhead. Es wird genau so viel Speicherplatz beanspruchen, wie die Plattform zum Speichern eines int verwendet (normalerweise 4 Byte auf aktuellen PC-Plattformen).
Dirk Holsopple
+1 für einen Java-Programmierer, der neugierig auf Speicher ist. Das Gedächtnisbewusstsein ist vor allem der wichtigste Faktor für die Leistung moderner Architekturen.
Imallett

Antworten:

15

Dies hängt von der Plattform und der Implementierung ab.

C ++ garantiert, dass die Größe von chargenau einem Byte und mindestens 8 Bit breit ist. Dann ist die Größe von a short intmindestens 16 Bit und nicht kleiner als char. Die Größe von a intist mindestens so groß wie die Größe von short int. Die Größe von long intbeträgt mindestens 32 Bit und ist nicht kleiner als int.

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

Das eigentliche Speichermodell von C ++ ist jedoch sehr kompakt und vorhersehbar . Zum Beispiel gibt es keine Metadaten in Objekten, Arrays oder Zeigern. Strukturen und Klassen sind genau wie Arrays zusammenhängend, aber das Auffüllen kann bei Bedarf und Bedarf platziert werden.

Ehrlich gesagt ist ein solcher Vergleich bestenfalls albern, da die Java-Speichernutzung mehr von der Java-Implementierung als vom ausgeführten Code abhängt.

zxcdw
quelle
1
Richtig, aber zum Vergleich würde ich sagen, wir sollten ganzzahlige Typen gleicher Größe annehmen (auch wenn sie nur unter verschiedenen Namen verfügbar sind). Unterschiedliche Größen bedeuten schließlich unterschiedliche Semantik, und auf vielen (nicht allen) gängigen Plattformen sind die Größen identisch, oder Ganzzahlen derselben Größe sind unter verschiedenen Namen verfügbar.
Hinweis für das OP: Sie sollten sich besser die Größe der Ganzzahl aussuchen. Wenn Sie ein 32-Bit-Int in C ++ möchten, können Sie es verwenden int32_t.
K.Steff
9

Die meisten Antworten scheinen ein paar ziemlich wichtige Punkte zu ignorieren.

Erstens sieht man in sehr viel Java praktisch nie ein Raw int- fast alle Verwendungszwecke sind von Nutzen Integer, so dass die Tatsache, dass ein int(ungefähr) die gleiche Größe wie ein intin C oder C ++ haben könnte, fast irrelevant ist, abgesehen davon ( Nach meiner Erfahrung ein kleiner Prozentsatz des Codes, der nur intanstelle von verwendet wird Integer.

Zweitens hat die Größe einzelner Objekte fast nichts mit dem Speicherbedarf eines gesamten Programms zu tun. In Java hängt der Speicherbedarf des Programms hauptsächlich davon ab, wie der Garbage Collector optimiert wurde. In den meisten Fällen wird der GC so eingestellt, dass die Geschwindigkeit maximiert wird, was (weitgehend) bedeutet, dass der GC so selten wie möglich ausgeführt wird.

Ich habe momentan keinen Link zur Hand, aber es gab einige Tests, die zeigten, dass Java mit der gleichen Geschwindigkeit wie C ausgeführt werden kann. Dazu muss der GC jedoch selten genug ausgeführt werden, damit er etwa siebenmal so viel verwendet Erinnerung. Das liegt nicht daran, dass einzelne Objekte siebenmal so groß sind, sondern daran, dass GC ziemlich teuer werden kann, wenn Sie es zu oft tun. Schlimmer noch, GC kann nur dann Speicher freigeben, wenn es "beweisen" kann, dass es keine Möglichkeit mehr gibt, auf ein Objekt zuzugreifen, und nicht nur, wenn Sie wissen, dass Sie damit fertig sind. Dies bedeutet, dass Sie, selbst wenn Sie den GC viel häufiger ausführen, um die Speichernutzung zu minimieren, wahrscheinlich immer noch ein typisches Programm mit einem größeren Speicherbedarf planen können. In einem solchen Fall können Sie den Faktor auf 2 oder 3 anstatt auf 7 reduzieren. Auch wenn Sie drastisch über Bord gehen, sollten Sie dies nicht tun.1 .

Abhängig von der Situation gibt es einen weiteren Faktor, der möglicherweise von Bedeutung ist oder nicht: den von der JVM selbst belegten Speicher. Dies ist mehr oder weniger behoben. Als Prozentsatz kann es sehr groß sein, wenn die App nicht viel eigenen Speicher benötigt, oder es kann winzig sein, wenn die App viel Speicherplatz benötigt. Zumindest auf meinem Computer scheint sogar die trivialste Java-App etwa 20 bis 25 Megabyte zu belegen (könnte für trivale Programme über 1000x oder für große Programme fast unermesslich klein sein).


1 Das heißt nicht, dass es niemandem gelingen könnte, Java mit einer Grundfläche zu schreiben, die der von C ++ entspricht. Es ist nur zu sagen , dass nur die gleiche Anzahl / Größe der Objekte aufweist und die GC läuft wirklich wird häufig nicht bekommen Sie dort in der Regel.

Jerry Sarg
quelle
7
Zu Ihrem ersten Punkt: Ich bin kein Java-Typ, aber die Java-APIs, die ich gesehen habe, wurden nie verwendet Integer(warum sollten sie?) Statt int. Nur generische Sammlungen haben keine andere Wahl, als sie Integeraufgrund der Typlöschung zu verwenden. Wenn Sie sich jedoch darum kümmern, können Sie diese durch eine Implementierung ersetzen, die auf den von intIhnen benötigten primitiven Typ spezialisiert ist . Und dann gibt es temporäres Boxen zum Durchlaufen von generischem Wrapping-Code (z. B. alles, was ein erfordert Object[]). Haben Sie außerdem Quellen für den GC-Speicherplatz? Ich bezweifle es nicht wirklich, ich bin nur neugierig.
9

Ich hoffe, Sie erkennen, dass all dies sowohl für Java als auch für C ++ tief in der Implementierung definiert ist. Das Objektmodell von Java benötigt jedoch viel Platz.

C ++ - Objekte benötigen (im Allgemeinen) keinen Speicher, außer dem, was die Mitglieder benötigen. Beachten Sie, dass (im Gegensatz zu Java, wo alles Benutzerdefinierte ein Referenztyp ist) der Clientcode ein Objekt sowohl als Werttyp als auch als Referenztyp verwenden kann, dh ein Objekt kann einen Zeiger / Verweis auf ein anderes Objekt speichern oder das Objekt direkt speichern ohne indirektion. Ein zusätzlicher Zeiger pro Objekt ist erforderlich, wenn es virtualMethoden gibt, aber einige nützliche Klassen sind so konzipiert, dass sie ohne Polymorphismus auskommen und dies nicht benötigen. Es gibt keine GC-Metadaten und keine Sperre pro Objekt. Daher class IntWrapper { int x; public: IntWrapper(int); ... };benötigen Objekte nicht mehr Platz als einfache ints und können direkt (dh ohne Indirektion) in Sammlungen und anderen Objekten platziert werden.

Arrays sind einfach deshalb schwierig, weil es in C ++ kein vorgefertigtes, allgemeines Äquivalent zu einem Java-Array gibt. Sie könnten einfach eine Reihe von Objekten zuweisen new[](ohne Overhead / Metadaten), aber es gibt kein Längenfeld - die Implementierung speichert wahrscheinlich eines, aber Sie können nicht darauf zugreifen. std::vectorist ein dynamisches Array und hat daher zusätzlichen Overhead und eine größere Schnittstelle. std::arrayund C-artige Arrays (int arr[N];) benötigen eine Kompilierungszeitkonstante. Theoretisch sollte es nur der Speicher des Objekts plus eine einzelne Ganzzahl für die Länge sein. Da Sie jedoch eine dynamische Größenänderung und eine voll funktionsfähige Benutzeroberfläche mit sehr wenig zusätzlichem Speicherplatz erhalten, sollten Sie dies in der Praxis einfach tun. Beachten Sie, dass alle diese und alle anderen Sammlungen standardmäßig die Objekte nach Wert speichern, wodurch Sie Indirektion und Platz für Referenzen sparen und das Cache-Verhalten verbessern. Sie müssen explizit Zeiger speichern (bitte intelligente), um eine Indirektion zu erhalten.

Die obigen Vergleiche sind nicht ganz fair, da einige dieser Einsparungen durch das Nichteinbeziehen von Funktionen erzielt werden, die Java enthält, und deren C ++ - Äquivalent häufig weniger optimiert ist als das Java-Äquivalent (*). Die übliche Art der Implementierung virtualin C ++ verursacht genau so viel Aufwand wie die übliche Art der Implementierung virtualin Java. Um eine Sperre zu erhalten, benötigen Sie ein Mutex-Objekt mit allen Funktionen, das höchstwahrscheinlich größer als einige Bits ist. Referenzzählung erhalten ( nichtEntspricht GC und sollte nicht als solches verwendet werden, ist aber manchmal nützlich. Sie benötigen einen intelligenten Zeiger, der ein Referenzzählfeld hinzufügt. Wenn das Objekt nicht sorgfältig erstellt wird, befinden sich Referenzanzahl, Smart-Pointer-Objekt und referenziertes Objekt an vollständig getrennten Positionen. Selbst wenn Sie es richtig erstellen, kann (muss?) Der gemeinsam genutzte Zeiger immer noch zwei Zeiger anstelle von einem sein. Andererseits verwendet ein guter C ++ - Stil diese Funktionen nicht genug, um eine Rolle zu spielen - in der Praxis verbrauchen die Objekte einer gut geschriebenen C ++ - Bibliothek weniger. Das bedeutet nicht unbedingt weniger Speicherauslastung insgesamt, aber es bedeutet, dass C ++ in dieser Hinsicht einen guten Vorsprung hat.

(*) Sie können beispielsweise virtuelle Anrufe, Identitäts-Hash-Codes und Sperren mit nur einem Wort für einige Objekte (und zwei Wörtern für viele andere Objekte) erhalten, indem Sie die Typinformationen mit verschiedenen Flags zusammenführen und Sperrbits für Objekte entfernen Es ist unwahrscheinlich, dass Schlösser benötigt werden. Eine ausführliche Erläuterung dieser und anderer Optimierungen finden Sie unter Raum- und zeiteffiziente Implementierung des Java-Objektmodells (PDF) von David F. Bacon, Stephen J. Fink und David Grove.


quelle
3

Eine Ebene intin Java benötigt genau so viel Speicherplatz wie eine intin C ++, vorausgesetzt, beide Implementierungen verwenden dieselbe Ganzzahlgröße und Speicherausrichtung.

Ein int 'object' (eine Ganzzahl mit Box, dh eine Instanz einer Klasse Integer) trägt den gesamten Overhead einer Klasseninstanz in Java, ist also erheblich größer als eine intin C ++. Wenn Sie jedoch ein Objekt in C ++ mit denselben Funktionen ausstatten, die Java-Objekte standardmäßig bieten (Polymorphismus, Boxen, Speicherbereinigung, RTTI), erhalten Sie wahrscheinlich ein gleichwertiges Objekt Größe.

Und dann gibt es Optimierungsüberlegungen; Da sich die Ausführungsmodelle und Programmierparadigmen unterscheiden, ist es unwahrscheinlich, dass ein nicht triviales Problem in beiden Sprachen gleich gelöst wird. Ein Vergleich der Speichergröße auf dieser Ebene ist daher nicht sehr sinnvoll.

Ja, Java-Objekte haben standardmäßig mehr Overhead als C ++ - Klassen, aber sie verfügen über mehr Funktionen, und dies führt zu einem anderen Programmierstil - ein guter Programmierer kann die Vor- und Nachteile beider Sprachen nutzen.

tdammers
quelle
+1 Mehr Overhead, aber mehr Funktionen in Java, ich verstehe jetzt, danke
Anthony