Unterschied zwischen Resident Set Size (RSS) und Java Total Committed Memory (NMT) für eine JVM, die im Docker-Container ausgeführt wird

Lesezeit: 7 Minuten

Benutzeravatar von sunsin1985
sunsin1985

Szenario:

Ich habe eine JVM, die in einem Docker-Container ausgeführt wird. Ich habe eine Speicheranalyse mit zwei Tools durchgeführt: 1) Spitze 2) Java Native Memory Tracking. Die Zahlen sehen verwirrend aus und ich versuche herauszufinden, was die Unterschiede verursacht.

Frage:

Der RSS wird als 1272 MB für den Java-Prozess gemeldet und der Gesamt-Java-Speicher wird mit 790,55 MB gemeldet. Wie kann ich erklären, wo der Rest des Speichers 1272 – 790,55 = 481,44 MB geblieben ist?

Warum ich dieses Problem offen halten möchte, auch nachdem ich mir diese Frage zu SO angesehen habe:

Ich habe die Antwort gesehen und die Erklärung macht Sinn. Nachdem Sie jedoch die Ausgabe von Java NMT und pmap -x erhalten haben, Ich bin immer noch nicht in der Lage, konkret zuzuordnen, welche Java-Speicheradressen tatsächlich resident und physisch zugeordnet sind. Ich brauche eine konkrete Erklärung (mit detaillierten Schritten), um herauszufinden, was diesen Unterschied zwischen RSS und Java Total Committed Memory verursacht.

Top-Ausgang

Geben Sie hier die Bildbeschreibung ein

Java-NMT

Geben Sie hier die Bildbeschreibung ein

Docker-Speicherstatistiken

Geben Sie hier die Bildbeschreibung ein

Grafiken

Ich habe einen Docker-Container, der länger als 48 Stunden läuft. Wenn ich jetzt ein Diagramm sehe, das Folgendes enthält:

  1. Dem Docker-Container zugewiesener Gesamtspeicher = 2 GB
  2. Java Max Heap = 1 GB
  3. Gesamt zugesichert (JVM) = immer weniger als 800 MB
  4. Heap Used (JVM) = immer weniger als 200 MB
  5. Non Heap Used (JVM) = immer weniger als 100 MB.
  6. RSS = ca. 1,1 GB.

Was frisst also den Speicher zwischen 1,1 GB (RSS) und 800 MB (Java Total Committed Memory)?

Geben Sie hier die Bildbeschreibung ein

  • Mögliches Duplikat von Warum meldet eine JVM mehr zugesicherten Speicher als die Größe des residenten Satzes des Linux-Prozesses? … naja, eine Umkehrung davon.

    – die8472

    27. Juli 2016 um 0:26 Uhr


  • Ich habe das gleiche Problem und finde die Antworten 🙁 Welche Art von Anwendung haben Sie?

    – Michael

    29. Juli 2016 um 11:10 Uhr

  • Fanden Sie die Antwort auf Ihre vorherige Frage (stackoverflow.com/a/38630406/6309) zufriedenstellend?

    – VonC

    29. Juli 2016 um 12:33 Uhr

  • @ sunsin1985 Sie könnten versuchen, die malloc-Implementierung in jemalloc zu ändern. Ich habe gesehen, dass RSS danach erheblich zurückgegangen ist. Sehen gdstechnology.blog.gov.uk/2015/12/11/… Ich habe eine Anwendung mit 600 MB Heap und 1,3 GB RSS – ich habe das noch nicht genau untersucht, aber der Speicher fehlt einfach – es ist kein signifikanter nativer Speicher zugewiesen. Ich vermute, dass dies an der Speicherfragmentierung liegt (und deshalb hilft jemalloc)

    – mabn

    4. August 2016 um 23:57 Uhr

  • Wenn Sie native Java-Speicherlecks verfolgen oder die RSS-Nutzung minimieren möchten, gibt es diese Frage mit Antworten: stackoverflow.com/questions/26041117/…

    – Lari Hotari

    7. September 2016 um 9:40 Uhr


Benutzeravatar von VonC
VonC

Sie haben eine Ahnung von “
Analysieren der Java-Speichernutzung in einem Docker-Container
” aus Michail Krestjaninow:

(Und um es klar zu sagen, im Mai 2019, drei Jahre später, die Situation verbessert sich mit openJDK 8u212 )

RBewohner Set Size ist die Menge an physischem Speicher, der derzeit von einem Prozess zugewiesen und verwendet wird (ohne ausgelagerte Seiten). Es umfasst den Code, die Daten und die gemeinsam genutzten Bibliotheken (die in jedem Prozess gezählt werden, der sie verwendet).

Warum unterscheiden sich die Docker-Statistikinformationen von den PS-Daten?

Die Antwort auf die erste Frage ist sehr einfach – Docker hat einen Fehler (oder ein Feature – je nach Laune): Es schließt Datei-Caches in die Informationen zur gesamten Speichernutzung ein. Wir können diese Metrik also einfach vermeiden und verwenden ps Informationen zu RSS.

Naja, ok – aber Warum ist RSS höher als Xmx?

Theoretisch im Falle einer Java-Anwendung

RSS = Heap size + MetaSpace + OffHeap size

wobei OffHeap aus Thread-Stacks, direkten Puffern, zugeordneten Dateien (Bibliotheken und JAR-Dateien) und JVM-Code selbst besteht

Seit JDK 1.8.40 wir haben Nativer Speicher-Tracker!

Wie Sie sehen können, habe ich bereits hinzugefügt -XX:NativeMemoryTracking=summary -Eigenschaft an die JVM, sodass wir sie einfach über die Befehlszeile aufrufen können:

docker exec my-app jcmd 1 VM.native_memory summary

(Das hat der OP gemacht)

Machen Sie sich keine Sorgen um den Abschnitt „Unbekannt“ – es scheint, dass NMT ein unausgereiftes Tool ist und nicht mit CMS GC umgehen kann (dieser Abschnitt verschwindet, wenn Sie einen anderen GC verwenden).

Merken Sie sich, dass NMT „festgeschriebenen“ Speicher anzeigt, nicht „resident“ (den Sie durch den Befehl ps erhalten). Mit anderen Worten, eine Speicherseite kann festgeschrieben werden, ohne dass sie als resident betrachtet wird (bis direkt darauf zugegriffen wird)..

Das bedeutet, dass NMT-Ergebnisse für Nicht-Heap-Bereiche (Heap ist immer vorinitialisiert) möglicherweise größer sind als RSS-Werte.

(Hier kommt “Warum meldet eine JVM mehr zugesicherten Speicher als die Größe des residenten Satzes des Linux-Prozesses?” ins Spiel.

Infolgedessen verbraucht unsere Anwendung trotz der Tatsache, dass wir das JVM-Heap-Limit auf 256 MB festgelegt haben, 367 MB. Die „anderen“ 164M werden hauptsächlich zum Speichern von Klassenmetadaten, kompiliertem Code, Threads und GC-Daten verwendet.

Die ersten drei Punkte sind oft Konstanten für eine Anwendung, sodass das einzige, was mit der Heap-Größe zunimmt, die GC-Daten sind.
Diese Abhängigkeit ist linear, aber die „k” Koeffizient (y = kx + b) ist viel kleiner als 1.


Allgemeiner scheint dies gefolgt zu sein Ausgabe 15020 die ein ähnliches Problem seit Docker 1.7 meldet

Ich verwende eine einfache Scala-Anwendung (JVM), die viele Daten in den und aus dem Speicher lädt.
Ich habe die JVM auf 8G Heap (-Xmx8G). Ich habe eine Maschine mit 132 GB Speicher und kann nicht mehr als 7-8 Container verarbeiten, da sie weit über die 8-GB-Grenze hinauswachsen, die ich der JVM auferlegt habe.

(docker stat War zuvor als irreführend gemeldetda es anscheinend Datei-Caches in die Informationen zur gesamten Speichernutzung einbezieht)

docker stat zeigt, dass jeder Container selbst viel mehr Speicher verwendet, als die JVM verwenden sollte. Zum Beispiel:

CONTAINER CPU % MEM USAGE/LIMIT MEM % NET I/O
dave-1 3.55% 10.61 GB/135.3 GB 7.85% 7.132 MB/959.9 MB
perf-1 3.63% 16.51 GB/135.3 GB 12.21% 30.71 MB/5.115 GB

Es scheint fast so, als würde die JVM das Betriebssystem nach Speicher fragen, der im Container zugewiesen wird, und die JVM gibt Speicher frei, während ihr GC ausgeführt wird, aber der Container gibt den Speicher nicht an das Hauptbetriebssystem zurück. Also … Speicherleck.

  • Danke für deine Antwort! Ich habe gerade eine Grafik an die ursprüngliche Frage angehängt. Die Sache ist die, gemäß der Zusammenfassung, die Sie bereitgestellt haben, wenn JVM das Betriebssystem nach Speicher gefragt hat, dann sollte RSS auch in die Höhe geschossen sein, wenn der verwendete Heap aufwächst. Und wenn GC passiert wäre, wäre der verwendete Heap heruntergekommen, aber das RSS wäre nicht passiert. Aber wenn ich mir die Diagramme ansehe, sehe ich, dass es von Anfang an eine beträchtliche Lücke zwischen RSS und Java gibt. Wie kann ich diese Lücke erklären?

    – sunsin1985

    4. August 2016 um 18:09 Uhr

  • @sunsin1985 “Was frisst den Speicher zwischen 1,1 GB (RSS) und 800 MB (Java Total Committed Memory)?” Ich vermute, dass dies die „Speicherklassen-Metadaten, kompilierter Code, Threads und GC-Daten“ sind, auf die im Artikel verwiesen wird, und GC gibt möglicherweise nichts frei, wenn der Container sie nicht weiterhin freigibt.

    – VonC

    4. August 2016 um 18:13 Uhr


  • danke, aber wird das nicht Teil der “gesamt zugesicherten” Zahl sein, die von Java NMT erhalten wird?

    – sunsin1985

    4. August 2016 um 18:42 Uhr

  • @sunsin1985 noch eine gute Lektüre: devcenter.heroku.com/articles/java-memory-issueswo die vollständige Java-Option wird JAVA_OPTS="-XX:NativeMemoryTracking=detail -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics"

    – VonC

    4. August 2016 um 19:08 Uhr

  • Ich denke, die Antwort hier ist nicht auf die gestellte Frage. Die Frage war, warum Java RSS > JVM heap+non_heap verarbeitet. Die Antwort ist auf die entgegengesetzte Frage: Warum JVM heap+non_heap > java process RSS sein könnte. @VonC fehlt mir etwas?

    – Artjom Nakonechny

    7. Juni 2019 um 19:06 Uhr


Disclaimer: Ich bin kein Experte

Ich hatte kürzlich einen Produktionsvorfall, als Pods unter hoher Last einen großen Sprung in RSS machten und Kubernetes die Pods beendete. Es gab keine OOM-Fehlerausnahme, aber Linux stoppte den Prozess auf die härteste Art und Weise.

Es gab eine große Lücke zwischen RSS und dem gesamten von JVM reservierten Speicherplatz. Heap-Speicher, nativer Speicher, Threads, alles sah gut aus, aber RSS war groß.

Es wurde herausgefunden, dass es daran liegt, wie malloc intern funktioniert. Es gibt große Lücken im Speicher, aus denen malloc Speicherblöcke entnimmt. Wenn sich viele Kerne auf Ihrem Computer befinden, versucht malloc, sich anzupassen und jedem Kern einen eigenen Speicherplatz zu geben, von dem er freien Speicher nehmen kann, um Ressourcenkonflikte zu vermeiden. Einrichten export MALLOC_ARENA_MAX=2 hat das Problem gelöst. Mehr zu dieser Situation finden Sie hier:

  1. Wachsende Nutzung des residenten Speichers (RSS) von Java Process
  2. https://devcenter.heroku.com/articles/tuning-glibc-memory-behavior
  3. https://www.gnu.org/software/libc/manual/html_node/Malloc-Tunable-Parameters.html
  4. https://github.com/jeffgriffith/native-jvm-leaks

PS Ich weiß nicht, warum es einen Sprung im RSS-Speicher gab. Pods basieren auf Spring Boot + Kafka.

1449620cookie-checkUnterschied zwischen Resident Set Size (RSS) und Java Total Committed Memory (NMT) für eine JVM, die im Docker-Container ausgeführt wird

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy