Frage Was verbraucht Speicher im Java-Prozess?


Wir versuchen, die Speicherauslastung des Java-Prozesses unter mäßiger Last zu untersuchen.

  PID   USER    PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  12663 test    20   0 8378m 6.0g 4492 S   43  8.4 162:29.95 java

Wie Sie sehen können, haben wir einen Speicher von 6 GB. Jetzt ist der interessante Teil das: Der Prozess wird mit diesen Parametern ausgeführt:

  • -Xmx2048m
  • -Xms2048m
  • -XX: Neue Größe = 512m
  • -XX: MaxDirectMemorySize = 256m
  • ... ein paar andere für GC und so

Wenn wir uns diese Einstellungen und die tatsächliche Speicherbelegung ansehen, sind wir überrascht, den Unterschied dessen zu sehen, was wir erwarten, dass dieser Prozess verwendet wird und was er tatsächlich verwendet.

Normalerweise werden unsere Speicherprobleme durch die Analyse von Heap-Dumps gelöst, aber in diesem Fall wird unser Speicher irgendwo außerhalb von Heap verwendet.

Fragen: Was wären die Schritte, um den Grund für eine so hohe Speichernutzung zu finden? Welche Tools könnten uns helfen herauszufinden, was den Speicher in diesem Prozess nutzt?

BEARBEITEN 0

Es sieht nicht so aus, als ob dies ein haufenbezogenes Problem ist, da wir dort noch ziemlich viel Platz haben:

jmap -heap 12663

Ergebnisse (bearbeitet um Platz zu sparen)

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize      = 2147483648 (2048.0MB)
NewSize          = 536870912 (512.0MB)
MaxNewSize       = 536870912 (512.0MB)
OldSize          = 1610612736 (1536.0MB)
NewRatio         = 7
SurvivorRatio    = 8
PermSize         = 21757952 (20.75MB)
MaxPermSize      = 85983232 (82.0MB)

New Generation: 45.7% used
Eden Space: 46.3% used
From Space: 41.4% used
To Space: 0.0% used
concurrent mark-sweep generation: 63.7% used
Perm Generation: 82.5% used

BEARBEITEN 1

Mit der Pmap können wir sehen, dass es eine Menge von 64MB gibt:

pmap -x 12663 | grep rwx | sort -n -k3 | less

Ergebnisse in:

... a lot more of these 64Mb chunks
00007f32b8000000       0   65508   65508 rwx--    [ anon ] <- what are these?
00007f32ac000000       0   65512   65512 rwx--    [ anon ]
00007f3268000000       0   65516   65516 rwx--    [ anon ]
00007f3324000000       0   65516   65516 rwx--    [ anon ]
00007f32c0000000       0   65520   65520 rwx--    [ anon ]
00007f3314000000       0   65528   65528 rwx--    [ anon ] 
00000000401cf000       0  241904  240980 rwx--    [ anon ] <- Direct memory ?
000000077ae00000       0 2139688 2139048 rwx--    [ anon ] <- Heap ?

Wie also herauszufinden, was sind diese 64Mb Brocken? Was benutzt sie? Welche Art von Daten sind in ihnen?

Vielen Dank


18
2017-12-16 11:27


Ursprung


Ich habe genau das gleiche Problem ... hier ist meine Frage. stackoverflow.com/questions/18734389/... Hast du eine Lösung? - DeepNightTwo
Siehe auch Wie groß ist der Speicherbedarf der JVM und wie kann ich diesen minimieren? - Vadzim


Antworten:


Das Problem könnte damit zusammenhängen Glib-Problem.

Wenn Sie mehrere Threads haben, die Speicher belegen, skaliert glibc grundsätzlich die Anzahl der verfügbaren Arenen für die Zuweisung, um Sperrkonflikte zu vermeiden. Eine Arena ist 64 MB groß. Die obere Grenze besteht darin, 8 Mal die Anzahl der Kerne Arenen zu erstellen. Arenen werden bei Bedarf erstellt, wenn ein Thread auf eine Arena zugreift, die bereits gesperrt ist, so dass sie mit der Zeit wächst.

In Java, wo Sie mit Threads streuen, kann dies schnell dazu führen, dass viele Arenen erstellt werden. Und es gibt Allokationen, die über diese Arenen verteilt sind. Anfangs wird jede 64Mb-Arena nur mit nicht zugeordnetem Speicher belegt, aber wenn Sie Zuweisungen vornehmen, beginnen Sie damit, tatsächlichen Speicher für sie zu verwenden.

Ihr Pmap hat wahrscheinlich ähnliche Einträge wie unten. Beachten Sie, wie 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. Das heißt, sie summieren sich zu 64 MB.

00007f4394000000 324K rw --- [anon]
00007f4394051000 65212K ----- [anon]
00007f4398000000 560K rw --- [anon]
00007f439808c000 64976K ----- [anon]
00007f439c000000 620K rw --- [anon]
00007f439c09b000 64916K ----- [anon]

Wie für Problemumgehungen: Der Bug erwähnt einige Umgebungsparameter, die Sie festlegen können, um die Anzahl der Arenen zu begrenzen, aber Sie benötigen eine glibc-Version, die hoch genug ist.


21
2017-11-13 13:32



Das Setzen von Xmx und Xms auf den gleichen Wert und das Setzen der Umgebungsvariable "export MALLOC_ARENA_MAX = 4" im sh-Skript, mit dem unser Web-Service gestartet wurde, haben in unserem Fall geholfen. Zuvor hatten wir alle 2 bis 8 Stunden Web-Service-Neustarts aufgrund von OOM Killer. Die GLIBC-Version in Ubuntu 14.04 ist 2,19, was gut ist, da es> = 2,16 sein muss, damit die MALLOC_ARENA_MAX-Einstellung funktioniert - Kluyg
Diese Antwort und der obige Kommentar waren ein Lebensretter für mich. In meinem Fall war MALLOC_ARENA_MAX = 1 notwendig und effektiv. - John Bachir


Wie wäre es mit Lambda-Sonde? Unter anderem kann es Speicherausfälle ähnlich wie im folgenden Screenshot zeigen:

Lambda Probe memory usage view

Manchmal pmap -x your_java_pid kann auch hilfreich sein.


3
2017-12-16 11:35



Danke für deine Antwort. Wenn ich es richtig verstehe, ist Lambda Probe für Apache Tomcat? Was wir nicht benutzen ... In Bezug auf die Pmap werde ich die Info zum Top Post hinzufügen - Konstantin S.


JProfiler könnte etwas sein, das du suchst, aber es ist nicht kostenlos. Ein weiteres gutes und kostenloses Tool zur Untersuchung der Speichernutzung von Java-Prozessen ist Java VisualVM, das als JDK-Tool in Oracle / Sun JDK-Distributionen verfügbar ist. Ich würde persönlich eine ganzheitlichere Herangehensweise an das Problem empfehlen (d. H. JDK + OS + -Disketten zu überwachen, usw.) - die Verwendung eines Netzwerküberwachungssystems - Nagios, Verax NMS oder OpenNMS.


2
2017-12-19 16:08



Sein Leck ist ein Haufen, also wird JProfiler hier nicht wirklich helfen - Asaf Mesika


Das Problem liegt außerhalb des Heap, also sind die besten Kandidaten:

JNI leak  
Allocation of direct memory buffer

Aufgrund der Tatsache, dass Sie die direkte Puffergröße begrenzt haben, ist der beste Kandidat meiner Meinung nach JNI-Leck.


2
2017-07-18 13:57





Es gibt ein praktisches Tool, um die Zuweisung des im JDK enthaltenen Heap-Speichers namens jmap anzuzeigen, darüber hinaus haben Sie auch Stack usw. (Xss). Führen Sie diese beiden jmap-Befehle aus, um weitere Informationen zur Speichernutzung zu erhalten:

jmap -heap <PID>
jmap -permstat <PID>

Um noch mehr Informationen zu erhalten, können Sie sich mit jconsole (ebenfalls im JDK enthalten) mit dem Prozess verbinden. Jconsole erfordert jedoch, dass JMX in der Anwendung konfiguriert wird.


1
2017-12-16 11:50



Danke für deine Antwort, aber das Problem scheint irgendwo außerhalb des Heaps zu liegen. Ich werde den oberen Beitrag aktualisieren, um einige Informationen aus jmap zu reflektieren - Konstantin S.
Wie viele Threads hat der Prozess? Die Stackgröße beträgt auf den meisten Plattformen 2 MB, also multiplizieren Sie diese mit der Anzahl der Threads. Ich wäre überrascht, wenn es für all die "fehlende" Erinnerung verantwortlich wäre, aber möglicherweise auch für einige davon. - HampusLi
Ungefähr 300 Threads, die von pmap übernommen wurden, haben eine Stackgröße von 1Mb. Und von der gleichen Pmap-Ausgabe sieht es nicht so aus, als würde einer dieser Stacks mehr als 100 Kb verbrauchen - Konstantin S.


Verwenden Sie JVisualVM. Es gibt verschiedene Ansichten, die Ihnen sagen, wie viel Heapspeicher verwendet wird, PermGen und so weiter.

Wie zur Beantwortung Ihrer Frage. Java verarbeitet Speicher anders als erwartet.

Wenn Sie die Parameter -Xms und -Xmx festlegen, teilen Sie der JVM mit, wie viel Speicher sie im Heapspeicher reservieren soll und wie viel sie maximal zuweisen soll.

Wenn Sie eine Java-Anwendung haben, die insgesamt 1 m Speicher verwendete, aber in -Xms256m -Xmx2g übergeben wurde, initialisiert die JVM sich selbst mit 256 m verwendetem Speicher. Es wird nicht weniger als das verwenden. Es spielt keine Rolle, dass Ihre Anwendung nur 1 m Speicher verwendet.

Zweitens. Im obigen Fall, wenn Sie zu einem bestimmten Zeitpunkt mehr als 256 m Speicher verwenden, wird die JVM so viel Arbeitsspeicher zuweisen, wie für die Bearbeitung der Anforderung erforderlich ist. Wie auch immer, es wird nicht Setzen Sie die Heap-Größe wieder auf den Mindestwert zurück. Zumindest nicht unter den meisten Umständen.

Da in Ihrem Fall der minimale und maximale Speicher auf 2 g eingestellt wird, weist die JVM beim Start 2 g zu und behält sie bei.

Die Java-Speicherverwaltung ist ziemlich komplex, und die Optimierung der Speichernutzung kann eine Aufgabe für sich sein. Es gibt jedoch viele Ressourcen, die helfen können.


0
2018-01-23 15:52