Was verbraucht Speicher im Java-Prozess?

19

Wir versuchen, die Speichernutzung des Java-Prozesses unter moderater 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 residenten Speicher von 6 GB. Nun ist der interessante Teil der folgende: Der Prozess wird mit diesen Parametern ausgeführt:

  • -Xmx2048m
  • -Xms2048m
  • -XX: NewSize = 512m
  • -XX: MaxDirectMemorySize = 256 m
  • ... einige andere für GC und so

Wenn wir uns diese Einstellungen und die tatsächliche Speichernutzung ansehen, sind wir erstaunt darüber, welchen Unterschied wir von diesem Prozess erwarten und was er tatsächlich verwendet.

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

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

EDIT 0

Es sieht nicht so aus, als wäre dies ein Heap-Problem, da wir dort noch ziemlich viel Platz haben:

jmap -heap 12663

Ergebnisse in (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

EDIT 1

Wenn wir die pmap benutzen, können wir sehen, dass es eine ganze Menge von Zuweisungen von 64 MB 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 kann man also herausfinden, was diese 64-MB-Chunks sind? Was benutzt sie? Welche Art von Daten sind in ihnen?

Vielen Dank

Konstantin S.
quelle
2
Ich habe genau das gleiche Problem ... hier ist meine Frage. stackoverflow.com/questions/18734389/… Haben Sie eine Lösung dafür?
DeepNightTwo

Antworten:

21

Das Problem hängt möglicherweise mit diesem Problem zusammen .

Wenn Sie mehrere Threads haben, die Speicher zuweisen, vergrößert glibc die Anzahl der verfügbaren Arenen, aus denen die Zuweisung erfolgt, um Sperrenkonflikte zu vermeiden. Eine Arena ist 64 MB groß. Die Obergrenze ist das 8-fache der Anzahl der Kerne. Arenen werden nach Bedarf erstellt, wenn ein Thread auf eine Arena zugreift, die bereits gesperrt ist, sodass sie mit der Zeit wächst.

In Java, wo Sie mit Threads bestreuen, kann dies schnell dazu führen, dass viele Arenen erstellt werden. Und es gibt Zuteilungen, die über diese Arenen verteilt sind. Anfänglich wird jeder 64-MB-Arena nur nicht festgelegter Speicher zugeordnet, aber wenn Sie Zuordnungen vornehmen, beginnen Sie, den tatsächlichen Speicher für diese zu verwenden.

Auf Ihrer pmap sind wahrscheinlich ähnliche Einträge zu finden. Beachten Sie, wie 324 K + 65212 K = 65536 K, 560 K + 64976 K = 65536 K, 620 K + 64916 K = 65536 K. Das heißt, sie summieren sich auf 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 Abhilfen : Der Fehler erwähnen einige Umgebungsparameter Sie festlegen können , um die Anzahl der Arenen zu begrenzen, aber Sie müssen hoch genug , um eine glibc Version.

Christian
quelle
5
Das Setzen von Xmx und Xms auf den gleichen Wert sowie das Setzen der Umgebungsvariablen "export MALLOC_ARENA_MAX = 4" im sh-Skript, das unseren Webdienst startet, hat in unserem Fall geholfen. Vorher hatten wir alle 2 bis 8 Stunden einen Neustart des Webservices durch OOM Killer. GLIBC-Version in Ubuntu 14.04 ist 2.19, was gut ist, da es> = 2.16 sein muss, damit die Einstellung MALLOC_ARENA_MAX 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
3

Wie wäre es mit Lamdba Probe ? Unter anderem kann es Ihnen Aufschlüsselungen der Speicherauslastung ähnlich dem folgenden Screenshot anzeigen:

Speicherbelegungsansicht der Lambda-Sonde

Manchmal pmap -x your_java_pidkann das auch hilfreich sein.

Janne Pikkarainen
quelle
Danke für deine Antwort. Wenn ich das richtig verstehe, ist Lambda Probe für Apache Tomcat? Was wir nicht benutzen ... In Bezug auf die pmap werde ich die Infos in den oberen Beitrag einfügen
Konstantin S.
2

JProfiler könnte etwas sein, was Sie suchen, 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 persönlich würde eine ganzheitlichere Herangehensweise an das Problem empfehlen (z. B. die Überwachung von JDK + OS + -Datenträgern usw.) - die Verwendung eines Netzwerküberwachungssystems - Nagios, Verax NMS oder OpenNMS.

Xantross
quelle
2
Sein Leck ist vom Haufen, also wird JProfiler hier nicht wirklich helfen
Asaf Mesika
2

Das Problem liegt außerhalb des Haufens, daher sind die besten Kandidaten:

JNI leak  
Allocation of direct memory buffer

Aufgrund der Tatsache, dass Sie die Größe des direkten Puffers begrenzt haben, ist meiner Meinung nach das JNI-Leck der beste Kandidat.

Venceslas
quelle
1

Es gibt ein praktisches Tool zum Anzeigen der Zuordnung des im JDK enthaltenen Heapspeichers, jmap genannt. Darüber hinaus gibt es einen Stapel 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 setzt jedoch voraus, dass JMX in der Anwendung konfiguriert wird.

HampusLi
quelle
Vielen Dank für Ihre Antwort, aber das Problem scheint irgendwo außerhalb des Haufens zu liegen. Ich werde den Top-Beitrag aktualisieren, um einige Informationen von jmap wiederzugeben
Konstantin S.
Wie viele Threads hat der Prozess? Die Stapelgröße beträgt auf den meisten Plattformen standardmäßig 2 MB. Multiplizieren Sie diese Größe mit der Anzahl der Threads. Es würde mich überraschen, wenn das alles "fehlende" Gedächtnis erklären würde, aber möglicherweise etwas davon.
HampusLi
Über 300 Threads, Informationen aus pmap entnommen, haben wir eine Stapelgröße von 1 MB. Und von der gleichen pmap-Ausgabe sieht es nicht so aus, als würde einer dieser Stacks mehr als 100 KB verbrauchen
Konstantin S.
0

Verwenden Sie JVisualVM. Es gibt verschiedene Ansichten, aus denen hervorgeht, wie viel Heap-Speicher verwendet wird, PermGen und so weiter und so fort.

Wie für die Beantwortung Ihrer Frage. Java handhabt den Speicher ganz anders als erwartet.

Wenn Sie die Parameter -Xms und -Xmx festlegen, teilen Sie der JVM mit, wie viel Speicher im Heap reserviert werden soll und wie viel maximal reserviert werden soll.

Wenn Sie eine Java-Anwendung haben, die insgesamt 1 MB Arbeitsspeicher verwendet, aber -Xms256m -Xmx2g übergeben hat, initialisiert sich die JVM mit 256 MB Arbeitsspeicher. Es wird nicht weniger als das verwenden. Es spielt keine Rolle, dass Ihre Anwendung nur 1 m Speicher benötigt.

Zweitens. In diesem Fall reserviert die JVM so viel Speicher, wie für die Bearbeitung der Anforderung erforderlich ist, wenn die App zu einem bestimmten Zeitpunkt mehr als 256 m Arbeitsspeicher verwendet. Die Größe des Heapspeichers wird jedoch nicht auf den Mindestwert zurückgesetzt. Zumindest nicht unter den meisten Umständen.

In Ihrem Fall weist die JVM zu Beginn 2 g zu und verwaltet diesen, da Sie den minimalen und maximalen Speicher auf 2 g festlegen.

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

drone.ah
quelle