Ich habe ein Problem mit einer Java-Anwendung, die unter Linux ausgeführt wird.
Wenn ich die Anwendung mit der standardmäßigen maximalen Heap-Größe (64 MB) starte, sehe ich bei Verwendung der obersten Anwendung, dass der Anwendung 240 MB virtueller Speicher zugewiesen sind. Dies führt zu Problemen mit anderer Software auf dem Computer, die relativ ressourcenbeschränkt ist.
Der reservierte virtuelle Speicher wird meines Wissens sowieso nicht verwendet, da bei Erreichen des Heap-Limits ein OutOfMemoryError
geworfen wird. Ich habe dieselbe Anwendung unter Windows ausgeführt und sehe, dass die Größe des virtuellen Speichers und die Größe des Heapspeichers ähnlich sind.
Gibt es überhaupt eine Möglichkeit, den für einen Java-Prozess unter Linux verwendeten virtuellen Speicher zu konfigurieren?
Edit 1 : Das Problem ist nicht der Heap. Das Problem ist, dass Linux, wenn ich beispielsweise einen Heap von 128 MB einstelle, immer noch 210 MB virtuellen Speicher zuweist, der niemals benötigt wird. **
Bearbeiten 2 : Mit ulimit -v
können Sie die Größe des virtuellen Speichers begrenzen. Wenn die eingestellte Größe unter 204 MB liegt, wird die Anwendung nicht ausgeführt, obwohl sie nicht 204 MB, sondern nur 64 MB benötigt. Ich möchte verstehen, warum Java so viel virtuellen Speicher benötigt. Kann das geändert werden?
Bearbeiten 3 : Es gibt mehrere andere Anwendungen im System, die eingebettet sind. Und das System hat ein virtuelles Speicherlimit (aus Kommentaren, wichtigen Details).
quelle
Antworten:
Dies ist eine langjährige Beschwerde bei Java, die jedoch weitgehend bedeutungslos ist und normalerweise auf der Suche nach falschen Informationen beruht. Die übliche Formulierung lautet etwa "Hallo Welt auf Java benötigt 10 Megabyte! Warum braucht es das?" Nun, hier ist eine Möglichkeit, Hello World auf einer 64-Bit-JVM dazu zu bringen, 4 Gigabyte zu übernehmen ... zumindest durch eine Messform.
Verschiedene Möglichkeiten zur Messung des Speichers
Unter Linux die Spitze gibt Ihnen Befehl mehrere verschiedene Nummern für den Speicher. Hier ist, was es über das Hello World-Beispiel sagt:
Die Situation für den Windows Task-Manager ist etwas komplizierter. Unter Windows XP gibt es Spalten "Speichernutzung" und "Größe des virtuellen Speichers", aber in der offiziellen Dokumentation wird nicht angegeben, was sie bedeuten. Windows Vista und Windows 7 fügen weitere Spalten hinzu, die tatsächlich dokumentiert sind . Von diesen ist die Messung "Arbeitssatz" am nützlichsten; es entspricht ungefähr der Summe von RES und SHR unter Linux.
Grundlegendes zur virtuellen Speicherzuordnung
Der von einem Prozess belegte virtuelle Speicher ist die Summe aller Elemente in der Prozessspeicherzuordnung. Dies umfasst Daten (z. B. den Java-Heap), aber auch alle vom Programm verwendeten gemeinsam genutzten Bibliotheken und Speicherzuordnungsdateien. Unter Linux können Sie den Befehl pmap verwenden , um alle Dinge anzuzeigen , die dem Prozessbereich zugeordnet sind (von nun an werde ich nur noch auf Linux verweisen, da es das ist, was ich verwende; ich bin sicher, dass es gleichwertige Tools gibt Windows). Hier ist ein Auszug aus der Speicherkarte des Programms "Hello World". Die gesamte Speicherkarte ist über 100 Zeilen lang und es ist nicht ungewöhnlich, eine Liste mit tausend Zeilen zu haben.
Eine kurze Erklärung des Formats: Jede Zeile beginnt mit der Adresse des virtuellen Speichers des Segments. Darauf folgen die Segmentgröße, die Berechtigungen und die Quelle des Segments. Dieses letzte Element ist entweder eine Datei oder "anon", was einen über mmap zugewiesenen Speicherblock angibt .
Von oben beginnend haben wir
java
). Das ist sehr klein; Alles, was es tut, ist das Laden in die gemeinsam genutzten Bibliotheken, in denen der echte JVM-Code gespeichert ist.-Xmx
Wert virtuellen Speicherplatz zuweist. Dies ermöglicht es ihm, einen zusammenhängenden Haufen zu haben. Der-Xms
Wert wird intern verwendet, um anzugeben, wie viel des Heapspeichers beim Starten des Programms "verwendet" wird, und um die Speicherbereinigung auszulösen, wenn diese Grenze erreicht wird.StackOverFlowError
. Bei einer echten App werden Dutzende, wenn nicht Hunderte dieser Einträge über die Speicherkarte wiederholt.Die gemeinsam genutzten Bibliotheken sind besonders interessant: Jede gemeinsam genutzte Bibliothek hat mindestens zwei Segmente: ein schreibgeschütztes Segment, das den Bibliothekscode enthält, und ein Lese- / Schreibsegment, das globale Prozessdaten für die Bibliothek enthält (ich weiß nicht, was das ist Segment ohne Berechtigungen ist; ich habe es nur unter x64 Linux gesehen). Der schreibgeschützte Teil der Bibliothek kann von allen Prozessen gemeinsam genutzt werden, die die Bibliothek verwenden.
libc
Verfügt beispielsweise über 1,5 MB virtuellen Speicherplatz, der gemeinsam genutzt werden kann.Wann ist die Größe des virtuellen Speichers wichtig?
Die virtuelle Speicherkarte enthält viele Dinge. Ein Teil davon ist schreibgeschützt, ein Teil wird gemeinsam genutzt und ein Teil wird zugewiesen, aber nie berührt (z. B. fast alle 4 GB Heap in diesem Beispiel). Das Betriebssystem ist jedoch intelligent genug, um nur das zu laden, was es benötigt, sodass die Größe des virtuellen Speichers weitgehend irrelevant ist.
Die Größe des virtuellen Speichers ist wichtig, wenn Sie auf einem 32-Bit-Betriebssystem arbeiten, auf dem Sie nur 2 GB (oder in einigen Fällen 3 GB) Prozessadressraum zuweisen können. In diesem Fall haben Sie es mit einer knappen Ressource zu tun und müssen möglicherweise Kompromisse eingehen, z. B. die Größe Ihres Heapspeichers, um eine große Datei im Speicher abzubilden oder viele Threads zu erstellen.
Angesichts der Tatsache, dass 64-Bit-Computer allgegenwärtig sind, wird es meiner Meinung nach nicht lange dauern, bis die Größe des virtuellen Speichers eine völlig irrelevante Statistik ist.
Wann ist die Größe des Resident-Sets wichtig?
Resident Set-Größe ist der Teil des virtuellen Speicherplatzes, der sich tatsächlich im RAM befindet. Wenn Ihr RSS zu einem bedeutenden Teil Ihres gesamten physischen Speichers wird, ist es möglicherweise an der Zeit, sich Sorgen zu machen. Wenn Ihr RSS wächst und Ihren gesamten physischen Speicher belegt und Ihr System mit dem Austausch beginnt, ist es längst vorbei, sich Sorgen zu machen.
RSS ist aber auch irreführend, insbesondere auf einer leicht geladenen Maschine. Das Betriebssystem ist nicht sehr aufwendig, um die von einem Prozess verwendeten Seiten zurückzugewinnen. Dies hat wenig Vorteile und das Potenzial für einen teuren Seitenfehler, wenn der Prozess die Seite in Zukunft berührt. Infolgedessen kann die RSS-Statistik viele Seiten enthalten, die nicht aktiv verwendet werden.
Endeffekt
Machen Sie sich keine allzu großen Sorgen darüber, was die verschiedenen Speicherstatistiken Ihnen sagen, es sei denn, Sie tauschen. Mit der Einschränkung, dass ein ständig wachsender RSS-Code auf eine Art Speicherverlust hinweisen kann.
Bei einem Java-Programm ist es viel wichtiger, darauf zu achten, was auf dem Heap passiert. Der Gesamtplatzbedarf ist wichtig, und Sie können einige Schritte unternehmen, um dies zu reduzieren. Wichtiger ist die Zeit, die Sie für die Speicherbereinigung aufwenden, und welche Teile des Heaps gesammelt werden.
Der Zugriff auf die Festplatte (dh eine Datenbank) ist teuer und der Speicher ist billig. Wenn Sie einen gegen den anderen tauschen können, tun Sie dies.
quelle
Es ist ein Problem mit Java und glibc> = 2.10 bekannt (einschließlich Ubuntu> = 10.04, RHEL> = 6).
Die Heilung besteht darin, diese Umgebung einzustellen. Variable:
Wenn Sie Tomcat ausführen, können Sie dies zur
TOMCAT_HOME/bin/setenv.sh
Datei hinzufügen .Fügen Sie dies für Docker zu Dockerfile hinzu
Es gibt einen IBM Artikel zum Festlegen von MALLOC_ARENA_MAX https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=de
Dieser Blog-Beitrag sagt
Es gibt auch einen offenen JDK-Fehler JDK-8193521 "glibc verschwendet Speicher mit Standardkonfiguration"
Suchen Sie bei Google oder SO nach MALLOC_ARENA_MAX, um weitere Referenzen zu erhalten.
Möglicherweise möchten Sie auch andere Malloc-Optionen optimieren, um eine geringe Fragmentierung des zugewiesenen Speichers zu optimieren:
quelle
MALLOC_ARENA_MAX
Ihr Speicherwachstum verlangsamen, jedoch nicht das Problem vollständig lösen.Die für den Java-Prozess zugewiesene Speichermenge entspricht weitgehend meinen Erwartungen. Ich hatte ähnliche Probleme beim Ausführen von Java auf eingebetteten / speicherbeschränkten Systemen. Das Ausführen von Anwendungen mit beliebigen VM-Grenzwerten oder auf Systemen, auf denen nicht genügend Swap vorhanden ist, kann zu Unterbrechungen führen. Es scheint die Natur vieler moderner Apps zu sein, die nicht für die Verwendung auf ressourcenbeschränkten Systemen konzipiert sind.
Sie haben einige weitere Optionen, mit denen Sie versuchen können, den Speicherbedarf Ihrer JVM zu begrenzen. Dies kann den Platzbedarf für den virtuellen Speicher verringern:
Außerdem sollten Sie Ihre -Xmx (maximale Heap-Größe) auf einen Wert einstellen, der so nahe wie möglich an der tatsächlichen maximalen Speichernutzung liegt Ihrer Anwendung liegt. Ich glaube, das Standardverhalten der JVM besteht immer noch darin, die Heap-Größe jedes Mal zu verdoppeln, wenn sie auf das Maximum erweitert wird. Wenn Sie mit 32 Millionen Heap beginnen und Ihre App einen Höchstwert von 65 Millionen erreicht, wächst der Heap um 32 Millionen -> 64 Millionen -> 128 Millionen.
Sie können dies auch versuchen, um die VM weniger aggressiv gegenüber dem Wachstum des Heaps zu machen:
Soweit ich mich vor einigen Jahren daran erinnere, hatte die Anzahl der geladenen nativen Bibliotheken einen großen Einfluss auf den minimalen Platzbedarf. Das Laden von java.net.Socket hat mehr als 15 Millionen hinzugefügt, wenn ich mich richtig erinnere (und ich wahrscheinlich nicht).
quelle
Die Sun JVM benötigt viel Speicher für HotSpot und wird in den Laufzeitbibliotheken im gemeinsam genutzten Speicher zugeordnet.
Wenn Speicherprobleme auftreten, sollten Sie eine andere JVM verwenden, die zum Einbetten geeignet ist. IBM hat j9 und es gibt das Open Source "jamvm", das GNU-Klassenpfadbibliotheken verwendet. Außerdem läuft auf Sun die Squeak JVM auf den SunSPOTS, sodass es Alternativen gibt.
quelle
Nur ein Gedanke, aber Sie können den Einfluss einer
ulimit -v
Option überprüfen .Dies ist keine tatsächliche Lösung, da dadurch der für alle Prozesse verfügbare Adressraum begrenzt wird. Auf diese Weise können Sie jedoch das Verhalten Ihrer Anwendung mit einem begrenzten virtuellen Speicher überprüfen.
quelle
Eine Möglichkeit, den Heap-Speicher eines Systems mit begrenzten Ressourcen zu reduzieren, besteht darin, mit der Variablen -XX: MaxHeapFreeRatio herumzuspielen. Dies ist normalerweise auf 70 eingestellt und ist der maximale Prozentsatz des Heaps, der frei ist, bevor der GC ihn verkleinert. Wenn Sie den Wert auf einen niedrigeren Wert einstellen, sehen Sie z. B. im jvisualvm-Profiler, dass für Ihr Programm normalerweise ein kleinerer Heap-Speicher verwendet wird.
BEARBEITEN: Um kleine Werte für -XX: MaxHeapFreeRatio festzulegen, müssen Sie auch -XX: MinHeapFreeRatio festlegen
EDIT2: Es wurde ein Beispiel für eine reale Anwendung hinzugefügt, die dieselbe Aufgabe startet und ausführt, eines mit Standardparametern und eines mit 10 und 25 als Parametern. Ich habe keinen wirklichen Geschwindigkeitsunterschied bemerkt, obwohl Java theoretisch mehr Zeit benötigen sollte, um den Heap im letzteren Beispiel zu erhöhen.
Am Ende beträgt der maximale Heap 905, der verwendete Heap 378
Am Ende beträgt der maximale Heap 722, der verwendete Heap 378
Dies hat tatsächlich einige Auswirkungen, da unsere Anwendung auf einem Remotedesktopserver ausgeführt wird und viele Benutzer sie möglicherweise gleichzeitig ausführen.
quelle
Suns Java 1.4 verfügt über die folgenden Argumente zur Steuerung der Speichergröße:
http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/java.html
Java 5 und 6 haben noch mehr. Siehe http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
quelle
Nein, Sie können die von der VM benötigte Speichermenge nicht konfigurieren. Beachten Sie jedoch, dass dies ein virtueller Speicher ist, der nicht resident ist, sodass er nur dort beschädigt bleibt, wenn er nicht tatsächlich verwendet wird.
Alternativ können Sie eine andere JVM als Sun One mit geringerem Speicherbedarf ausprobieren, aber ich kann hier nicht raten.
quelle