Nutzung des virtuellen Speichers von Java unter Linux, zu viel Speicher

259

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 OutOfMemoryErrorgeworfen 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 -vkö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).

Mario Ortegón
quelle
Warum beschäftigen Sie sich mit der Nutzung des virtuellen Speichers? Wenn Sie wirklich besorgt sein möchten, sehen Sie sich die Nutzung des residenten Speichers an und lesen Sie die folgenden Befehle: free, ps, top.
Basszero
2
Im System werden mehrere andere Anwendungen ausgeführt, die eingebettet sind. Und das System hat ein virtuelles Speicherlimit.
Mario Ortegón
ahhhh, der Teufel steckt im Detail
Basszero
Welche Java-Implementierung verwenden Sie? IIRC, die kostenlose Sun JRE ohne Moor-Standard (nicht OpenJDK), ist nicht für die eingebettete Verwendung lizenziert.
Tom Hawtin - Tackline
Ich glaube, ich habe den "eingebetteten" Teil
falsch verwendet

Antworten:

630

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.

java -Xms1024m -Xmx4096m com.example.Hello

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:

  PID BENUTZER PR NI VIRT RES SHR S% CPU% MEM ZEIT + BEFEHL
 2120 kgregory 20 0 4373 m 15 m 7152 S 0 0,2 0: 00,10 java
  • VIRT ist der virtuelle Speicherplatz: die Summe aller Elemente in der virtuellen Speicherzuordnung (siehe unten). Es ist weitgehend bedeutungslos, außer wenn es nicht ist (siehe unten).
  • RES ist die Größe des residenten Satzes: Die Anzahl der Seiten, die derzeit im RAM resident sind. In fast allen Fällen ist dies die einzige Zahl, die Sie verwenden sollten, wenn Sie "zu groß" sagen. Aber es ist immer noch keine sehr gute Zahl, besonders wenn es um Java geht.
  • SHR ist die Menge des residenten Speichers, die mit anderen Prozessen gemeinsam genutzt wird. Bei einem Java-Prozess ist dies normalerweise auf gemeinsam genutzte Bibliotheken und JAR-Dateien mit Speicherzuordnung beschränkt. In diesem Beispiel wurde nur ein Java-Prozess ausgeführt, daher vermute ich, dass 7k ein Ergebnis von Bibliotheken ist, die vom Betriebssystem verwendet werden.
  • SWAP ist standardmäßig nicht aktiviert und wird hier nicht angezeigt. Es gibt die Menge des virtuellen Speichers an, der sich derzeit auf der Festplatte befindet, unabhängig davon , ob er sich tatsächlich im Swap-Bereich befindet oder nicht . Das Betriebssystem ist sehr gut darin, aktive Seiten im RAM zu halten, und die einzigen Möglichkeiten zum Austauschen sind (1) mehr Speicher zu kaufen oder (2) die Anzahl der Prozesse zu reduzieren. Daher ist es am besten, diese Anzahl zu ignorieren.

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.

0000000040000000 36K rx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040108000 8K rwx-- /usr/local/java/jdk-1.6-x64/bin/java
0000000040eba000 676K rwx-- [anon]
00000006fae00000 21248K rwx-- [anon]
00000006fc2c0000 62720K rwx-- [anon]
0000000700000000 699072K rwx-- [anon]
000000072aab0000 2097152K rwx-- [anon]
00000007aaab0000 349504K rwx-- [anon]
00000007c0000000 1048576K rwx-- [anon]
...
00007fa1ed00d000 1652K r-xs- /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar
...
00007fa1ed1d3000 1024K rwx-- [anon]
00007fa1ed2d3000 4K ----- [anon]
00007fa1ed2d4000 1024K rwx-- [anon]
00007fa1ed3d4000 4K ----- [anon]
...
00007fa1f20d3000 164K rx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f20fc000 1020K ----- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
00007fa1f21fb000 28K rwx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so
...
00007fa1f34aa000 1576K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000 2044K ----- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000 16K rx-- /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000 4K rwx-- /lib/x86_64-linux-gnu/libc-2.13.so
...

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

  • Der JVM-Loader (dh das Programm, das bei der Eingabe ausgeführt wird 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.
  • Eine Reihe von Anon-Blöcken, die den Java-Heap und interne Daten enthalten. Dies ist eine Sun-JVM, daher ist der Heap in mehrere Generationen unterteilt, von denen jede einen eigenen Speicherblock hat. Beachten Sie, dass die JVM basierend auf dem -XmxWert virtuellen Speicherplatz zuweist. Dies ermöglicht es ihm, einen zusammenhängenden Haufen zu haben. Der -XmsWert 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.
  • Eine JAR-Datei mit Speicherzuordnung, in diesem Fall die Datei, die die "JDK-Klassen" enthält. Wenn Sie eine JAR im Speicher abbilden, können Sie sehr effizient auf die darin enthaltenen Dateien zugreifen (anstatt sie jedes Mal von Anfang an zu lesen). Die Sun-JVM ordnet alle JARs im Klassenpfad zu. Wenn Ihr Anwendungscode auf eine JAR zugreifen muss, können Sie diese auch im Speicher abbilden.
  • Pro-Thread-Daten für zwei Threads. Der 1M-Block ist der Thread-Stapel. Ich hatte keine gute Erklärung für den 4k-Block, aber @ericsoe identifizierte ihn als "Schutzblock": Er hat keine Lese- / Schreibberechtigungen, verursacht also einen Segmentfehler, wenn darauf zugegriffen wird, und die JVM fängt diesen ab und übersetzt es zu einem StackOverFlowError. Bei einer echten App werden Dutzende, wenn nicht Hunderte dieser Einträge über die Speicherkarte wiederholt.
  • Eine der gemeinsam genutzten Bibliotheken, die den tatsächlichen JVM-Code enthält. Es gibt mehrere davon.
  • Die gemeinsam genutzte Bibliothek für die C-Standardbibliothek. Dies ist nur eines von vielen Dingen, die die JVM lädt und die nicht ausschließlich Teil von Java sind.

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. libcVerfü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.

kdgregory
quelle
9
Sie sollten berücksichtigen, dass Teile des Speichers, die derzeit ausgelagert sind, in der RES-Messung fehlen. Möglicherweise haben Sie einen niedrigen RES-Wert, aber nur, weil die Anwendung inaktiv war und ein Großteil des Heapspeichers auf die Festplatte ausgelagert wurde. Java macht einen sehr schlechten Job beim Auslagern: Auf jedem vollständigen GC wird der größte Teil des Heaps durchlaufen und kopiert. Wenn sich also ein Großteil Ihres Heaps im Swap befand, muss der GC alles wieder in den Hauptspeicher laden.
Jrudolph
1
Tolle Antwort kdgregory! Ich arbeite in einer eingebetteten Umgebung mit einer CF, die KEINEN Swap Space hat. Basierend auf Ihrer Antwort stammen alle meine VIRT-, SWAP- und nFLT-Werte aus speicherabgebildeten Dateien ... was jetzt Sinn macht, zu miauen. Wissen Sie, ob der SWAP-Wert Seiten darstellt, die noch nicht in den Speicher geladen wurden, oder Seiten, die aus dem Speicher ausgelagert wurden, oder beides? Wie können wir uns ein Bild von möglichem Thrashing machen (fortlaufende Karte ein- und austauschen)?
Jeach
2
@Jeach - Ich war überrascht, dass ein Tausch gemeldet wurde, also startete ich mein "reisendes Linux" (ein USB-Stick mit Ubuntu 10.04 und ohne Tausch). Als ich die Spalte "SWAP" oben aktivierte , sah ich, dass Eclipse 509 m hatte. Als ich es dann mit pmap betrachtete , betrug der gesamte virtuelle Raum 650 m. Ich vermute also, dass die Zahl "SWAP" alle Seiten auf der Festplatte darstellt, nicht nur diejenigen, die sich nicht im Speicher befinden.
kdgregory
2
Was Ihre zweite Frage betrifft: Wenn Sie ständig Seiten von der Flash-Karte lesen, sollte Ihre E / A-Wartezeit (in der Zusammenfassung oben als "% wa" angezeigt ) hoch sein. Beachten Sie jedoch, dass dies für jede Aktivität, insbesondere für Schreibvorgänge, hoch ist (vorausgesetzt, Ihr Programm führt eine aus).
kdgregory
1
> Der 1M-Block ist ein Thread-Stapel. Ich weiß nicht, was in den 4K-Block geht. Der 4K-Block, der weder Lese- noch Schreibberechtigungen aufweist, ist wahrscheinlich ein Schutzblock. Beim Stapelüberlauf wird auf diesen Bereich zugegriffen, wodurch ein Fehler ausgelöst wird, den die JVM durch Generieren einer Java StackOverflowException behandeln kann. Dies ist weitaus billiger als das Überprüfen des Stapelzeigers bei jedem Methodenaufruf. Schutzbereiche ohne festgelegte Berechtigungen können auch in anderen Kontexten verwendet werden.
Eriksoe
38

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:

export MALLOC_ARENA_MAX=4

Wenn Sie Tomcat ausführen, können Sie dies zur TOMCAT_HOME/bin/setenv.shDatei hinzufügen .

Fügen Sie dies für Docker zu Dockerfile hinzu

ENV MALLOC_ARENA_MAX=4

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 ist bekannt, dass sich der residente Speicher auf ähnliche Weise wie ein Speicherverlust oder eine Speicherfragmentierung einschleicht.

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:

# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536
Lari Hotari
quelle
Diese Antwort hat mir auf einem 64-Bit-Ubuntu-Server mit einem TomEE-Server wirklich geholfen, der ein wenig zu "mem-konsumierend" wurde. Der Link zum IBM-Artikel ist wirklich eine tiefgreifende Erklärung. Nochmals vielen Dank für diesen guten Hinweis!
MWiesner
1
Die JVM verliert möglicherweise nativen Speicher, was zu ähnlichen Symptomen führt. Siehe stackoverflow.com/a/35610063/166062 . Nicht geschlossene GZIPInputStream- und GZIPOutputStream-Instanzen können ebenfalls eine Ursache für das Leck sein.
Lari Hotari
3
In Java 8 gibt es einen JVM-Fehler, der zu einem unbegrenzten nativen Speicherwachstum führt: bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8164293 - Wenn dies Sie betrifft, kann die Verwendung MALLOC_ARENA_MAXIhr Speicherwachstum verlangsamen, jedoch nicht das Problem vollständig lösen.
Outofcoffee
@LariHotari schätzen wirklich Ihre Bemühungen, auf glibc und redhat Version hinzuweisen
Sam
2
Java 8u131 enthält einen Backport-Bugfix für den zugehörigen JVM-Fehler JDK-8164293 bugs.openjdk.java.net/browse/JDK-8178124 .
Lari Hotari
9

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:

-XX: ReservedCodeCacheSize = 32m Reservierte Code-Cache-Größe (in Bytes) - maximale Code-Cache-Größe. [Solaris 64-Bit, amd64 und -Server x86: 48 m; in 1.5.0_06 und früher Solaris 64-Bit und and64: 1024m.]

-XX: MaxPermSize = 64 m Größe der permanenten Generation. [5.0 und neuer: 64-Bit-VMs sind 30% größer skaliert; 1,4 amd64: 96 m; 1.3.1-Client: 32 m.]

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:

-XX: MinHeapFreeRatio = 40 Minimaler Prozentsatz an Heap, der nach der GC frei ist, um eine Erweiterung zu vermeiden.

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).

James Schek
quelle
7

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.

Thorbjørn Ravn Andersen
quelle
Ist es eine Option, den Hot Spot zu deaktivieren?
Mario Ortegón
Vielleicht. Überprüfen Sie die Befehlszeilenoptionen für die von Ihnen verwendete JVM.
Thorbjørn Ravn Andersen
3

Nur ein Gedanke, aber Sie können den Einfluss einer ulimit -vOption ü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.

VonC
quelle
Genau das ist mein Problem. Mein Heap ist auf 64 MB eingestellt, aber Linux reserviert 204 MB. Wenn ich das ulimit unter 204 setze, wird die Anwendung überhaupt nicht ausgeführt.
Mario Ortegón
Interessant: Das Festlegen des ulimit kann für andere Prozesse unbeabsichtigte Nebenwirkungen haben und erklären, warum die Anwendung nicht ausgeführt werden kann.
VonC
Das Problem scheint zu sein, dass Java diese größere Menge an virtuellem Speicher reservieren muss, obwohl es nicht verwendet wird. In Windows sind der verwendete virtuelle Speicher und die Xmx-Einstellung eher näher beieinander.
Mario Ortegón
Haben Sie es mit einer JRockit JVM versucht?
VonC
Da die Speicherzuordnung der JVM die Summe aus der Heap-Zuordnung und der Perm-Größe ist (die erste kann mit den Optionen -Xms und -Xmx festgelegt werden), haben Sie einige Einstellungen mit -XX: PermSize und -XX: MaxPermSize versucht (Standard von 32 MB bis 64 MB, abhängig von der JVM-Version)?
VonC
3

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

java -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=25 HelloWorld

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.

Standardparameter

Am Ende beträgt der maximale Heap 905, der verwendete Heap 378

MinHeap 10, MaxHeap 25

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.

runholen
quelle
1

Suns Java 1.4 verfügt über die folgenden Argumente zur Steuerung der Speichergröße:

-Xmsn Geben Sie die Anfangsgröße des Speicherzuordnungspools in Byte an. Dieser Wert muss ein Vielfaches von 1024 größer als 1 MB sein. Fügen Sie den Buchstaben k oder K hinzu, um Kilobyte anzugeben, oder m oder M, um Megabyte anzugeben. Der Standardwert ist 2 MB. Beispiele:

           -Xms6291456
           -Xms6144k
           -Xms6m

-Xmxn Geben Sie die maximale Größe des Speicherzuordnungspools in Byte an. Dieser Wert muss ein Vielfaches von 1024 größer als 2 MB sein. Fügen Sie den Buchstaben k oder K hinzu, um Kilobyte anzugeben, oder m oder M, um Megabyte anzugeben. Der Standardwert ist 64 MB. Beispiele:

           -Xmx83886080
           -Xmx81920k
           -Xmx80m

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

Paul Tomblin
quelle
1
Das Problem, das ich habe, ist nicht mit der Heap-Größe, sondern mit der Menge an virtuellem Speicher, die von Linux zugewiesen wird
Mario Ortegón
Lesen Sie die Erklärung von kdgregory. Durch Reduzieren der Heap-Größe, der "neuen Größe" und der anderen konfigurierbaren Parameter wird die Menge an REAL-Speicher reduziert, die der JVM benötigt.
Paul Tomblin
Er kann ein legitimes Problem haben. Einige Anwendungen (wie eine, die ich geschrieben habe) ordnen eine 1-GB-Datei zu, und einige Systeme verfügen nur über 2 GB virtuellen Speicher, von denen einige mit gemeinsam genutzten Bibliotheken gefüllt werden. Und wenn dies das Problem ist, sollte er die DSO-Randomisierung definitiv deaktivieren. Es gibt eine Option in / proc.
Zan Lynx
0

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.

Marko
quelle