2010-01-20 8 views
7

Ich habe eine Bibliothek in C geschrieben, die viel Speicher verbraucht (Millionen von kleinen Blöcken). Ich habe ein c-Programm geschrieben, das diese Bibliothek verwendet. Und ich habe ein Java-Programm geschrieben, das dieselbe Bibliothek verwendet. Das Java-Programm ist eine sehr dünne Schicht um die Bibliothek herum. Grundsätzlich gibt es nur eine native Methode, die aufgerufen wird, die ganze Arbeit erledigt und Stunden später zurückkehrt. Es gibt keine weitere Kommunikation zwischen Java und der nativen Bibliothek über die Java-Aufrufschnittstelle. Es gibt auch keine Java-Objekte, die eine nennenswerte Speichermenge verbrauchen.Warum verwendet eine native Bibliothek 1,5 Mal mehr Speicher, wenn sie von Java verwendet wird, als wenn sie von einem C-Programm unter Linux verwendet wird?

Also sind das c-Programm und das Java-Programm sehr ähnlich. Die gesamte Berechnung/Speicherzuordnung erfolgt innerhalb der nativen Bibliothek. Immer noch. Wenn es ausgeführt wird, verbraucht das c-Programm 3 GB Arbeitsspeicher. Aber das Java-Programm verbraucht 4,3 GB! (VIRT-Menge von oben gemeldet)

Ich überprüfte die Speicherkarte des Java-Prozesses (mit Pmap). Nur 40 MB werden von Bibliotheken verwendet. Daher sind zusätzliche Bibliotheken, die von Java geladen werden, nicht die Ursache.

Hat jemand eine Erklärung für dieses Verhalten?

EDIT: Danke für die Antworten bisher. Um es ein wenig klarer zu machen: Der Java-Code ruft nur die native Bibliothek auf ONCE! Der Java-Heap hat die Standardgröße (vielleicht 60 MB) und wird nicht verwendet (außer für die eine Klasse, die die Hauptmethode enthält, und für die andere Klasse, die die native Bibliothek aufruft).

Die native Bibliotheksmethode ist eine lange laufende und tut viele Mallocs und Freigaben. Fragmentierung ist eine Erklärung, die ich auch an mich selbst dachte. Da jedoch kein Java-Code aktiv ist, sollte das Fragmentierungsverhalten für das Java-Programm und das c-Programm gleich sein. Da es anders ist, nehme ich auch an, dass die verwendeten malloc-Implementierungen unterschiedlich sind, wenn sie im c-Programm oder im Java-Programm ausgeführt werden.

+0

Interessante Beobachtung. Ich hätte dieses Verhalten nicht erwartet. – x4u

+0

Malloc Heap Fragmentierung. Siehe http://stackoverflow.com/a/28935176/166062 –

+0

@LariHotari Während der Grund für diese Frage eine andere war (siehe meine Antwort), hatten wir tatsächlich auch Probleme mit der Fragmentierung. Wir haben es gelöst, indem wir zu alternativen Implementierungen wie tcmalloc, jemalloc oder der malloc-Implementierung von locklessinc gewechselt haben. –

Antwort

2

Sorry Jungs. Falsche Annahmen.

Ich habe mich an die 64 MB der Sun Java-Implementierungen gewöhnt, die für die standardmäßige maximale Heap-Größe verwendet wurden. Aber ich habe openjdk 1.6 zum Testen benutzt. Openjdk verwendet einen Bruchteil des physischen Arbeitsspeichers, wenn keine maximale Heap-Größe explizit angegeben wurde. In meinem Fall ein Viertel. Ich habe eine 4GB-Maschine benutzt. Ein Viertel ist also 1GB. Da ist der Unterschied zwischen C und Java.

Leider ist dieses Verhalten nirgends dokumentiert. Ich fand es mit Blick auf den Quellcode von openjdk (arguments.cpp):

// If the maximum heap size has not been set with -Xmx, 
// then set it as fraction of the size of physical memory, 
// respecting the maximum and minimum sizes of the heap. 
0

Es gibt verschiedene Faktoren, die Sie besonders in einer Sprache wie Java berücksichtigen müssen, Java wird auf einer virtuellen Maschine ausgeführt und Garbage Collection wird von der Java Runtime gehandhabt, da es (wie ich mir vorstellen würde) eine erhebliche Anstrengung kostet die Java-Aufrufschnittstelle, um die native Methode innerhalb der nativen Bibliothek umzuschalten oder auszuführen, da es eine Möglichkeit geben müsste, Speicherplatz auf dem Stapel zuzuweisen, zu systemeigenem Code zu wechseln, die native Methode auszuführen, zurück zur virtuellen Java-Maschine zu wechseln und vielleicht irgendwie , der Platz auf dem Stapel wurde nicht frei - das würde ich gerne denken.

Hoffen, dass dies hilft, Mit freundlichen Grüßen, Tom.

3

nur raten: Sie können eine Nicht-Standard-malloc Implementierung verwenden, wenn sie innerhalb der JVM, die Melodie auf die specfic Bedürfnisse der JVM läuft und produziert mehr Aufwand als die Universal malloc in Ihrer normalen libc-Implementierung.

+0

Das wäre meine Vermutung. Aber ich gebe dir nicht die +1, bis es Beweise dafür gibt, dass es wirklich so ist. – Omnifarious

1

Java muss über einen kontinuierlichen Speicher für seinen Heap verfügen, damit es die maximale Speichergröße als virtuellen Speicher zuweisen kann. Dies verbraucht jedoch keinen physischen Speicher und verbraucht möglicherweise nicht einmal Swap. Ich würde prüfen, um wie viel Ihr Bewohner Speicher erhöht sich um.

+0

Bibliotheken, die von der JVM aufgerufen werden, können den Speicher jedoch weiterhin freigeben. –

+0

Eine Möglichkeit besteht darin, dass ein Java-Thread gleichzeitig mit dem systemeigenen Code ausgeführt wird und den Speicher fragmentiert. Das OP sagt, dass es viele kleine Zuweisungen gibt. – Omnifarious

+0

Die maximale Heap-Größe für Java ist auf 60 MB oder so eingestellt. Dies kann nicht der Grund sein, 1 GB mehr Speicher zu verbrauchen. Still: resident Speicher ist 3GB, wie das c-Programm. –

0

Es ist schwer zu sagen, aber ich denke, im Kern des Problems ist, dass es zwei Heaps in Ihrer Anwendung, die gepflegt werden müssen - die Standard-Java-Heap für Java-Objektzuweisungen (von der JVM verwaltet), und der C-Heap, der durch Aufrufe von malloc/free verwaltet wird. Es ist schwer zu sagen, was genau passiert, ohne etwas Code zu sehen.

0

Hier ist ein Vorschlag zur Bekämpfung.

Lassen Sie den C-Code mit dem Standard-malloc-Aufruf stoppen und verwenden Sie eine alternative Version von malloc, die Speicher durch mmap ing /dev/zero ergreift. Sie können entweder eine Implementierung von malloc aus einer Bibliothek ändern oder Ihre eigenen Rollen erstellen, wenn Sie sich dafür kompetent genug fühlen.

Ich vermute stark, dass Sie feststellen werden, dass Ihr Problem verschwindet, nachdem Sie das getan haben.