Frage

Ich habe ein Problem mit einer Java-Anwendung unter Linux ausgeführt wird.

Wenn ich die Anwendung zu starten, mit dem Standard maximale Heap-Größe (64 MB), sehe ich die Spitzen-Anwendung mit, dass 240 MB virtuellen Speicher der Anwendung zugewiesen werden. Das schafft einige Probleme mit einer anderen Software auf dem Computer, die relativ ressourcen beschränkt.

Der reservierte virtuelle Speicher wird sowieso nicht verwendet werden, soweit ich das verstehen, denn einmal erreichen wir die Haufen begrenzen ein OutOfMemoryError geworfen wird. Ich lief die gleiche Anwendung unter Windows, und ich sehe, dass die Größe des virtuellen Speichers und der Heap-Größe sind ähnlich.

Gibt es trotzdem, dass ich den virtuellen Speicher in Verwendung für einen Java-Prozess unter Linux konfigurieren können?

Bearbeiten 1 : Das Problem ist nicht der Heap. Das Problem ist, dass wenn ich einen Haufen von 128 MB, zum Beispiel festgelegt, noch Linux 210 MB virtueller Speicher reserviert, die nicht benötigt wird, je. **

Edit 2 : ulimit -v Verwendung erlaubt die Größe des virtuellen Speichers zu begrenzen. Wenn die Größe Satz unter 204 MB ist, dann wird die Anwendung nicht ausgeführt werden, obwohl es nicht 204 MB benötigt nur 64 MB. Deshalb möchte ich verstehen, warum Java so viel virtuellen Speicher erfordert. Kann dies geändert werden?

Bearbeiten 3 : Es gibt mehrere andere Anwendungen in dem System ausgeführt werden, das eingebettet ist. Und das System funktioniert eine virtuelle Speicherlimit (von Kommentaren, wichtiges Detail).

War es hilfreich?

Lösung

Das war eine langjährige Beschwerde mit Java, aber es ist weitgehend bedeutungslos, und basieren in der Regel auf der falschen Informationen zu suchen. Die übliche Formulierung ist so etwas wie „Hallo Welt auf Java dauert 10 Megabyte! Warum es das braucht?“ Nun, hier ist ein Weg, Hallo Welt auf einem 64-Bit-JVM Anspruch zu nehmen mehr als 4 Gigabyte zu machen ... zumindest durch eine Form der Messung.

java -Xms1024m -Xmx4096m com.example.Hello

Different Ways to Measure-Speicher

Unter Linux die oben Befehl gibt Ihnen verschiedene Zahlen für das Gedächtnis. Hier ist, was es sagt über die Hallo Welt Beispiel:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 2120 kgregory  20   0 4373m  15m 7152 S    0  0.2   0:00.10 java
  • VIRT ist die virtuelle Speicherplatz: die Summe von allem in der virtuellen Speicherkarte (siehe unten). Es ist weitgehend bedeutungslos, es sei denn, es ist nicht (siehe unten).
  • RES ist der Resident Set-Größe: die Anzahl der Seiten, die sich momentan im RAM sind. In fast allen Fällen ist dies die einzige Zahl, die Sie verwenden sollten, wenn ich sage „zu groß.“ Aber es ist noch nicht eine sehr gute Zahl, vor allem wenn es um Java.
  • SHR ist die Menge an residenten Speicher, der mit anderen Prozessen gemeinsam genutzt wird. Für einen Java-Prozess, wird dies in der Regel auf gemeinsam genutzte Bibliotheken und Memory-Mapped JARfiles begrenzt. In diesem Beispiel hatte ich nur einen Java-Prozess ausgeführt, so dass ich vermute, dass die 7k ein Ergebnis der Bibliotheken durch das O verwendet wird.
  • SWAP ist standardmäßig nicht aktiviert ist, und wird hier nicht gezeigt. Es gibt die Größe des virtuellen Speichers, die auf der Festplatte zur Zeit aufhalten, ob oder nicht, es ist tatsächlich in dem Swap-Bereich . Das Betriebssystem ist sehr gut über aktive Seiten im RAM zu halten, und die einzigen Heilmittel für den Austausch sind (1) mehr Speicher kaufen, oder (2) reduzieren die Anzahl der Prozesse, so ist es am besten, diese Zahl zu ignorieren.

Die Situation für Windows Task-Manager ist ein bisschen komplizierter. Unter Windows XP gibt es "Speichernutzung" und "Virtual Memory Size" Spalten, aber die dokumentiert . Von diesen ist die „Working Set“ Messung der nützlichste; es entspricht in etwa der Summe von RES und SHR unter Linux.

Das Verständnis der Virtual Memory Map

Die virtuelle durch einen Prozess verbrauchen Speicher ist die Summe all das, was in der Prozess Speicherkarte ist. Dazu gehören Daten (zB die Java-Heap), sondern auch alle gemeinsam genutzten Bibliotheken und Memory-Mapped-Dateien, die von dem Programm verwendet. Unter Linux können Sie den pmap Befehl all die Dinge in den Prozess abgebildet sehen Raum (von hier aus werde ich nur auf Linux beziehen, weil es ist, was ich, ich bin sicher, dass es gleichwertige Tools für Windows). Hier ist ein Auszug aus der Speicherkarte des „Hallo Welt“ Programms; die gesamte Speicherkarte ist über 100 Zeilen lang, und es ist nicht ungewöhnlich, dass eine tausend Online-Liste zu haben.

0000000040000000     36K r-x--  /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 r-x--  /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 r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3634000   2044K -----  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3833000     16K r-x--  /lib/x86_64-linux-gnu/libc-2.13.so
00007fa1f3837000      4K rwx--  /lib/x86_64-linux-gnu/libc-2.13.so
...

Eine kurze Erläuterung des Formats: Jede Zeile beginnt mit der virtuellen Speicheradresse des Segments. Dies wird durch die Segmentgröße, Berechtigungen gefolgt, und die Quelle des Segments. Dieser letzte Punkt ist entweder eine Datei oder „Anon“, die einen Speicherblock über Mmap .

Beginnend von oben, haben wir

  • Die JVM-Loader (dh das Programm, das ausgeführt wird, wenn Sie java Typ). Dies ist sehr klein; alle es tut, ist Last in den gemeinsam genutzten Bibliotheken, in denen der realen JVM-Code gespeichert ist.
  • Ein Bündel von Anon blockiert die Java Heap und interne Daten zu halten. Dies ist eine Sun JVM, so dass der Haufen ist broken in mehrere Generationen, von denen jeder Block einem eigenen Speicher ist. Beachten Sie, dass die JVM virtuellen Speicherplatz reserviert auf dem -Xmx Wert basiert; dies ermöglicht es einen zusammenhängenden Haufen zu haben. Der -Xms Wert wird intern verwendet, zu sagen, wie viel von der Halde „in Gebrauch“ ist, wenn das Programm startet, und die Garbage Collection auslösen wie die Grenze genähert wird.
  • Eine Memory-Mapped jarfile, in diesem Fall wird die Datei, die das hält "JDK-Klassen." Wenn Sie eine JAR-Speicher-Karte, können Sie die Dateien darin zugreifen sehr effizient (gegenüber von Anfang an jedes Mal zu lesen). Die Sun JVM-Speicher-Karte alle JAR-Dateien auf dem Classpath; wenn Ihr Anwendungscode eine JAR zugreifen muss, können Sie auch speicher wo es.
  • Per-Thread-Daten für zwei Threads. Der Block 1 M ist ein Thread-Stack; Ich weiß nicht, was in den 4K-Block geht. Für eine echte App, werden Sie Dutzende sehen, ob nicht Hunderte dieser Einträge durch die Speicherkarte wiederholt.
  • Eine der gemeinsam genutzten Bibliotheken, die die tatsächliche JVM-Code enthält. Es gibt mehrere davon.
  • Die gemeinsam genutzte Bibliothek für die C-Standardbibliothek. Dies ist nur eines von vielen Dingen, dass die JVM Lasten, die nicht unbedingt Teil von Java sind.

Die gemeinsam genutzten Bibliotheken sind besonders interessant: Jede gemeinsam genutzte Bibliothek mindestens zwei Segmente: ein Read-Only-Segment des Bibliothekscode enthält, und ein Schreib-Lese-Segment, die globalen Pro-Prozessdaten für die Bibliothek enthält (ich weiß nicht wissen, was das Segment ohne Berechtigungen ist, ich habe es nur auf x64 Linux gesehen). Der Nur-Lese-Teil der Bibliothek kann zwischen allen Prozessen, die die Bibliothek verwenden, gemeinsam genutzt werden; zum Beispiel hat libc 1.5M von virtuellem Speicherraum, die gemeinsam genutzt werden kann.

Wann ist virtuelle Speichergröße wichtig?

Die virtuelle Speicherkarte enthält eine Menge Sachen. Ein Teil davon ist schreibgeschützt, ein Teil davon wird geteilt, und ein Teil davon zugewiesen aber nie berührt (zB fast alle der 4 GB Heap in diesem Beispiel). Aber das Betriebssystem ist intelligent genug, um nur zu laden, was er braucht, so dass die Größe des virtuellen Speichers ist weitgehend irrelevant.

Wo virtuelle Speichergröße wichtig ist, ist, wenn Sie auf ein 32-Bit-Betriebssystem laufen lassen, in dem Sie 2Gb nur zuordnen können (oder, in einigen Fällen 3Gb) von Prozessadressraum. In diesem Fall, dass Sie mit einer knappen Ressource zu tun haben, und vielleicht Kompromisse machen müssen, wie Ihre Heap-Größe, um die Speicher-Karte, eine große Datei oder erstellen Sie eine Menge Threads zu reduzieren.

Aber, da die 64-Bit-Maschinen allgegenwärtig sind, ich glaube nicht, dass es lange dauern, bis virtuelle Speichergröße eine völlig irrelevant Statistik ist.

Wann ist Resident Set Size wichtig?

Resident Set Größe ist der Teil des virtuellen Speicherraum, die tatsächlich im RAM ist. Wenn Ihr RSS ein wesentlicher Teil Ihres gesamten physischen Speichers sein wächst, könnte es Zeit sein, sich Gedanken zu starten. Wenn Ihr RSS alle Ihre physischen Speicher nehmen aufwächst, und Ihr System Swapping gestartet, es ist auch höchste Zeit sich Gedanken zu starten.

Aber RSS ist auch irreführend, vor allem auf einer leicht belastete Maschine. Das Betriebssystem nicht viel Mühe aufwenden, um die Seiten durch ein Verfahren verwendet, um Rückgewinnung. Es gibt kaum einen Nutzen, indem Sie so, und das Potenzial für einen teueren Seitenfehler gewonnen werden, wenn der Prozess der Seite in Zukunft berührt. Als Ergebnis kann die RSS-Statistik viele Seiten enthält, die nicht im aktiven Gebrauch ist.

Bottom Line

Wenn Sie nicht gerade tauschen, erhalten nicht allzu besorgt über das, was die verschiedenen Speicher Statistiken, die Sie sagen. Mit dem Vorbehalt, dass ein ständig wachsenden RSS irgendeine Art von Speicherverlust hinweisen.

Mit einem Java-Programm, es ist viel wichtiger, darauf zu achten, was in dem Heap passiert. Die Gesamtmenge an Platz verbraucht ist wichtig, und es gibt einige Schritte, die Sie, dass reduzieren nehmen. Wichtiger ist die Menge an Zeit, die Sie in der Garbage Collection verbringen, und welchen Teile des Haufens c bekommenollected.

Zugriff auf die Festplatte (dh eine Datenbank) ist teuer und Speicher ist billig. Wenn Sie eine für den anderen handeln können, tun dies.

Andere Tipps

Es ist ein bekanntes Problem mit Java und glibc> = 2,10 (enthält Ubuntu> = 10.04, RHEL> = 6).

Die Heilung ist diese env einzustellen. Variable:

export MALLOC_ARENA_MAX=4

Wenn Sie Tomcat einsetzen, können Sie diese Datei hinzufügen TOMCAT_HOME/bin/setenv.sh.

Für Docker, fügen Sie diese Dockerfile

ENV MALLOC_ARENA_MAX=4

Es ist ein IBM-Artikel über MALLOC_ARENA_MAX Einstellung 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-Eintrag sagt

  

residenten Speicher wurde in einer ähnlichen Weise wie ein Kriechen bekannt   Speicherleck oder Speicherfragmentierung.

Es gibt auch einen offenen JDK Bug JDK-8193521 „glibc Abfälle Speicher mit Standardkonfiguration „

Suche nach MALLOC_ARENA_MAX auf Google oder SO Weitere Referenzen.

Sie können wollen, stimmen auch andere malloc Optionen für niedrige Fragmentierung der zugewiesenen Speicher 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

Die Menge an Speichern für den Java-Prozess zugewiesen ist so ziemlich auf Augenhöhe mit dem, was ich erwarten würde. Ich habe ähnliche Probleme hat Java auf eingebetteten / Speicher begrenzt Systeme ausgeführt werden. Ausführen von jeder Anwendung mit beliebigen VM Grenzen oder auf Systeme, die dazu neigen, zu brechen nicht ausreichende Mengen an Swap haben. Es scheint, die Natur von vielen modernen Anwendungen zu sein, die nicht für die auf ressourcenbegrenzten Systemen Verwendung entwerfen werden.

Sie haben ein paar mehr Optionen können Sie versuchen, und Ihren Speicherbedarf JVM zu begrenzen. Dies könnte die virtuelle Speicher-Fußabdruck reduzieren:

  

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

     

-XX: MaxPermSize = 64m Größe der Permanent Generation. [5.0 und neuer:   64-Bit-VMs werden 30% größer skaliert; 1.4   amd64: 96m; 1.3.1 -client. 32m]

Außerdem sollten Sie auch Ihre -Xmx (max Heap-Größe) auf einen Wert so nahe wie möglich an die tatsächliche maximale Speichernutzung gesetzt Ihrer Anwendung. Ich glaube, das Standardverhalten der JVM ist noch zu Doppel die Heap-Größe jedes Mal, wenn es bis zum Maximum erweitert werden. Wenn Sie mit 32M Haufen beginnen und Ihre App ihren Höhepunkt erreicht 65M, dann würde der Haufen wachsenden 32M am Ende -> 64M -.> 128M

Sie können auch versuchen, diese die VM weniger aggressiv zu wachsen die Haufen zu machen:

  

-XX: MinHeapFreeRatio = 40 Mindestanteil des Heap frei nach GC   vermeiden Expansion.

Auch von dem, was ich mich erinnere von vor mit diesem ein paar Jahren experimentiert, geladen die Zahl der nativen Bibliotheken hatte einen großen Einfluss auf den minimalen Platzbedarf. Laden java.net.Socket hinzugefügt mehr als 15M, wenn ich richtig (und ich wahrscheinlich nicht) wieder zu verwenden.

The Sun JVM erfordert viel Speicher für HotSpot und es Karten in den Laufzeitbibliotheken im gemeinsam genutzten Speicher.

Wenn der Speicher ein Problem betrachten unter Verwendung eines anderen JVM geeignet für die Einbettung ist. IBM hat j9, und es ist die Open Source "jamvm", die Bibliotheken GNU Classpath verwendet. Auch Sun hat die Squeak JVM auf den SunSpots läuft so gibt es Alternativen.

Nur ein Gedanke, aber Sie können den Einfluss von eine ulimit -v Option .

Das ist keine wirkliche Lösung, da sie Adressraum für verfügbaren begrenzen würden alle Prozess, aber das würde ermöglicht es Ihnen, das Verhalten Ihrer Anwendung mit einem begrenzten virtuellen Speichern zu überprüfen.

Eine Möglichkeit, den Haufen sice ein System mit begrenzten Ressourcen zu reduzieren sein kann mit der -XX zu spielen, um: MaxHeapFreeRatio variabel. Dies wird in der Regel auf 70, und ist der maximale Prozentsatz des Haufens, die frei ist, bevor der GC schrumpft. Eine Einstellung auf einen niedrigeren Wert, und Sie werden zum Beispiel der jvisualvm Profiler sehen, dass ein kleiner Haufen sice in der Regel für das Programm verwendet wird.

EDIT: Um kleine Werte für -XX gesetzt: MaxHeapFreeRatio müssen Sie setzen auch -XX: MinHeapFreeRatio Eg

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

EDIT2: ein Beispiel für eine echte Anwendung hinzugefügt, das beginnt, und hat die gleiche Aufgabe, einen mit Standard-Parametern und eine mit 10 und 25 als Parameter. Ich habe nicht bemerkt, keine wirkliche Geschwindigkeitsdifferenz, obwohl Java in der Theorie sollte mehr Zeit verwenden, um die Haufen in dem letzteren Beispiel zu erhöhen.

Default Parameter

Am Ende, max Heap 905, verwendet Heap ist 378

MinHeap 10, MaxHeap 25

Am Ende, max Heap 722, verwendet Heap ist 378

Dies ist eigentlich etwas inpact haben, wie unsere Anwendung auf einem Remote-Desktop-Server ausgeführt wird, und viele Benutzer kann es auf einmal ausgeführt werden.

Sun Java 1.4 hat die folgenden Argumente Speichergröße zu steuern:

  

-Xmsn       Geben Sie die Anfangsgröße in Byte der Speicherzuweisung Pool.   Dieser Wert muss ein Vielfaches von 1024   größer als 1 MB. Fügen Sie die Buchstaben k   oder K, um anzuzeigen, Kilobyte oder m oder M   um anzuzeigen Megabyte. Der Standard   Wert ist 2 MB. Beispiele:

           -Xms6291456
           -Xms6144k
           -Xms6m
     

-Xmxn       Geben Sie die maximale Größe in Bytes der Speicherzuweisung Pool.   Dieser Wert muss ein Vielfaches von 1024   größer als 2 MB. Fügen Sie die Buchstaben k   oder K, um anzuzeigen, Kilobyte oder m oder M   um anzuzeigen Megabyte. Der Standard   Wert ist 64MB. Beispiele:

           -Xmx83886080
           -Xmx81920k
           -Xmx80m

http://java.sun.com /j2se/1.4.2/docs/tooldocs/windows/java.html

Java 5 und 6 haben einige mehr. Siehe http://java.sun.com/javase/technologies/hotspot/vmoptions .jsp

Nein, Sie können nicht Speichermenge konfigurieren, indem Sie VM benötigt. Beachten Sie jedoch, dass dieser virtuelle Speicher ist, die keinen Wohnsitz, also bleibt es nur da, ohne Schaden, wenn nicht tatsächlich verwendet wird.

Alernatively, können Sie einige anderen JVM dann eine Sun versuchen, mit geringerem Speicherbedarf, aber ich kann hier nicht beraten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top