web-dev-qa-db-ger.com

Überwachen der Nicht-Heap-Speichernutzung einer JVM

Wir behandeln normalerweise OutOfMemoryError-Probleme aufgrund eines Konfigurationsproblems der Heap- oder Permgengröße.

Der gesamte Speicher der JVM ist jedoch nicht permgen oder heap . Soweit ich weiß, kann es sich auch um Threads/Stacks, nativen JVM-Code handeln.

Aber mit pmap kann ich sehen, dass der Prozess mit 9.3G belegt ist, was 3,3G Speicherplatzauslastung bedeutet.

Ich frage mich, welche Möglichkeiten bestehen, diesen zusätzlichen Speicherbedarf außerhalb des Heapspeichers zu überwachen und einzustellen. 

Ich verwende keinen direkten Speicherzugriff außerhalb des Heapspeichers (MaxDirectMemorySize ist standardmäßig 64m).

Context: Load testing
Application: Solr/Lucene server
OS: Ubuntu
Thread count: 700
Virtualization: vSphere (run by us, no external hosting)

JVM

Java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)

Tunning

-Xms=6g
-Xms=6g
-XX:MaxPermSize=128m

-XX:-UseGCOverheadLimit
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSClassUnloadingEnabled

-XX:+OptimizeStringConcat
-XX:+UseCompressedStrings 
-XX:+UseStringCache 

Speicherkarten:

https://Gist.github.com/slorber/5629214

vmstat

procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 1  0   1743    381      4   1150    1    1    60    92    2    0  1  0 99  0

kostenlos

             total       used       free     shared    buffers     cached
Mem:          7986       7605        381          0          4       1150
-/+ buffers/cache:       6449       1536
Swap:         4091       1743       2348

Oben

top - 11:15:49 up 42 days,  1:34,  2 users,  load average: 1.44, 2.11, 2.46
Tasks: 104 total,   1 running, 103 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.5%us,  0.2%sy,  0.0%ni, 98.9%id,  0.4%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   8178412k total,  7773356k used,   405056k free,     4200k buffers
Swap:  4190204k total,  1796368k used,  2393836k free,  1179380k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                                                                 
17833 jmxtrans  20   0 2458m 145m 2488 S    1  1.8 206:56.06 Java                                                                                                                                    
 1237 logstash  20   0 2503m 142m 2468 S    1  1.8 354:23.19 Java                                                                                                                                    
11348 Tomcat    20   0 9184m 5.6g 2808 S    1 71.3 642:25.41 Java                                                                                                                                    
    1 root      20   0 24324 1188  656 S    0  0.0   0:01.52 init                                                                                                                                    
    2 root      20   0     0    0    0 S    0  0.0   0:00.26 kthreadd             
...

df -> tmpfs

Filesystem                1K-blocks     Used Available Use% Mounted on
tmpfs                       1635684      272   1635412   1% /run

Das Hauptproblem haben wir:

  • Der Server verfügt über 8 GB physischen Speicher
  • Der Haufen von Solr dauert nur 6G
  • Es gibt 1,5 G Swap
  • Swappiness = 0
  • Der Haufenverbrauch scheint entsprechend getunnelt zu sein
  • Auf dem Server ausgeführt: Nur Solr und einige Überwachungssachen
  • Wir haben eine korrekte durchschnittliche Antwortzeit
  • Wir haben manchmal ungewöhnlich lange Pausen, bis zu 20 Sekunden

Ich vermute, die Pausen könnten eine volle GC auf einem getauschten Haufen sein, oder?

Warum gibt es so viel Swap?

Ich weiß nicht einmal, ob dies die JVM ist, durch die der Server ausgetauscht wird, oder ob etwas versteckt ist, das ich nicht sehen kann. Vielleicht der OS-Seiten-Cache? Aber nicht sicher, warum das Betriebssystem Seiten-Cache-Einträge erstellen würde, wenn dies zu einem Auslagerungsvorgang führt.

Ich überlege, den mlockall-Trick zu testen, der in einigen beliebten Java-basierten Speichern/NoSQL-Programmen wie ElasticSearch, Voldemort oder Cassandra verwendet wird: check JVM/Solr nicht mit mlockall austauschen


Bearbeiten:

Hier sehen Sie Max Heap, Used Heap (Blau), einen gebrauchten Swap (Rot). Es scheint irgendwie verwandt zu sein.

Swap and Heap

Ich kann mit Graphite sehen, dass viele ParNew GC regelmäßig auftreten. Und es gibt einige CMS-GCs, die den erheblichen Abnahmen des Bilds entsprechen.

Die Pausen scheinen nicht mit der Abnahme des Heapspeichers zu korrelieren, werden aber regelmäßig zwischen 10:00 und 11:30 Uhr verteilt, daher kann es sich um die ParNew GC handeln, denke ich.

Während des Belastungstests kann ich einige Disc-Aktivitäten und auch einige Swap IO -Aktivitäten sehen, die nach Beendigung des Tests wirklich ruhig sind.

29

Ihr Heapspeicher verwendet tatsächlich 6,5 GB virtuellen Speicher (dies kann die Dauergeneration einschließen)

Sie haben eine Reihe von Threads mit 64-MB-Stapeln. Nicht klar, warum einige und die Standardeinstellung 1 MB verwenden.

Die Gesamtsumme beträgt 9,3 Millionen KB an virtuellem Speicher. Ich würde mich nur um die Einwohnergröße kümmern.

Verwenden Sie top, um die residente Größe des Prozesses zu ermitteln.

Sie können dieses Programm nützlich finden

    BufferedReader br = new BufferedReader(new FileReader("C:/dev/gistfile1.txt"));
    long total = 0;
    for(String line; (line = br.readLine())!= null;) {
        String[] parts = line.split("[- ]");
        long start = new BigInteger(parts[0], 16).longValue();
        long end = new BigInteger(parts[1], 16).longValue();
        long size = end - start + 1;
        if (size > 1000000)
            System.out.printf("%,d : %s%n", size, line);
        total += size;
    }
    System.out.println("total: " + total/1024);

Wenn Sie nicht über eine JNI-Bibliothek verfügen, die den Speicher verwendet, haben Sie vermutlich viele Threads, die jeweils einen eigenen Stack-Speicherplatz haben. Ich würde die Anzahl der Threads überprüfen, die Sie haben. Sie können den maximalen Stapelspeicherplatz pro Thread reduzieren. Eine bessere Option könnte jedoch die Reduzierung der Anzahl der Threads sein.

Der Off-Heap-Speicher ist definitionsgemäß nicht verwaltet, so dass er als solcher nicht einfach "abgestimmt" wird. Selbst das Anpassen der Menge ist nicht einfach.

Die Standardstapelgröße für 64-Bit-JVMs beträgt 1024 KB, sodass 700 Threads 700 MB virtuellen Speicher verwenden.

Sie sollten die Größe des virtuellen Speichers für die Größe des residenten Speichers nicht verwechseln. Der virtuelle Speicher einer 64-Bit-Anwendung ist nahezu frei und hat nur die Größe der residenten Benutzer, um die Sie sich sorgen müssen.

So wie ich es sehe, haben Sie insgesamt 9,3 GB.

  • 6,0 GB Heap.
  • 128 MB Dauergen
  • 700 MB-Stapel.
  • <250 gemeinsam genutzte Bibliotheken
  • 2,2 GB unbekannt (ich vermute, dass virtueller Speicher kein residenter Speicher ist)

Das letzte Mal, als jemand dieses Problem hatte, hatten sie viel mehr Threads, als sie wollten. Ich würde die maximale Anzahl der Threads überprüfen, die Sie hatten, da der Peak die virtuelle Größe bestimmt. z.B. War es näher an 3000?


Hmmm jedes dieser Paare ist ein Gewinde.

7f0cffddf000-7f0cffedd000 rw-p 00000000 00:00 0 
7f0cffedd000-7f0cffee0000 ---p 00000000 00:00 0

und diese schlagen vor, dass Sie jetzt etwas weniger als 700 Threads haben .....

9
Peter Lawrey

Eine recht bequeme Möglichkeit, die Laufzeitparameter einer JVM-Instanz zu überwachen (und teilweise zu ändern), ist VisualVM:

PS
(gelöscht)

PPS ... Ich erinnerte mich an das andere Werkzeug, das ich vor einiger Zeit verwendet hatte: Visual GC . Es zeigt Ihnen visuell im Detail, was in der JVM-Speicherverwaltung passiert, hier einige Screenshots . Sehr leistungsfähig und kann sogar mit einem Plugin in VisualVM integriert werden (siehe Abschnitt Plugins auf der VisualVM-Homepage).

PPPS
We sometimes have anormaly long pauses, up to 20 seconds. [...] I guess the pauses could be a full GC on a swapped heap right?
Ja, das könnte sein. Diese langen Pausen könnten auch bei nicht getauschten Heaps durch vollständige GC verursacht werden. Mit VisualVM können Sie überwachen, ob eine vollständige GC in dem Moment erfolgt, in dem die Pause von ~ 20 Sekunden stattfindet. Ich empfehle, VisualVM auf einem anderen Host auszuführen und es über explizite JMX mit dem JVM-Prozess auf Ihrem virtuellen Server zu verbinden, um die Messungen nicht mit zusätzlicher Belastung zu verfälschen. Sie können das Setup über Tage/Wochen laufen lassen und somit endgültige Informationen zu diesem Phänomen sammeln.

Afaics mit aktuellen Informationen, im Moment gibt es nur diese Möglichkeiten:

  • die beobachteten Pausen treten gleichzeitig mit der vollständigen GC auf: Die JVM ist nicht richtig eingestellt. Sie können dies über JVM-Parameter abmildern und möglicherweise einen anderen GC-Algorithmus/eine andere Engine auswählen (haben Sie CMS und G1 GC ausprobiert. Weitere Informationen dazu, wie dies geschieht, zum Beispiel hier ).
  • die beobachteten Pausen stimmen nicht mit einer vollständigen GC in der JVM überein: Der physische virtuelle Host kann die Ursache sein. Überprüfen Sie Ihre SLAs (wie viel virtuell RAM garantiert im physischen RAM ist) und wenden Sie sich an Ihren Dienstanbieter, um den virtuellen Server zu überwachen. 

Ich hätte erwähnen sollen, dass VisualVM mit Java ausgeliefert wird. Und JConsole, ebenfalls mit Java ausgeliefert, ist leichter und kompakter als VisualVM (hat jedoch keine Plugins, kein Profiling usw.), bietet jedoch eine ähnliche Übersicht.

Wenn das Einrichten der JMX-Verbindung für VisualVM/JConsole/VisualGC im Moment zu kompliziert ist, können Sie auf folgende Java-Parameter zurückgreifen: -XX:+PrintGC -XX:+PrintGCTimeStamps -Xloggc:/my/log/path/gclogfile.log. Diese Parameter bewirken, dass die JVM für jeden GC-Lauf einen Eintrag in die angegebene Protokolldatei schreibt. Diese Option eignet sich auch für Langzeitanalysen und ist wahrscheinlich die mit dem geringsten Aufwand für Ihre JVM.

Nachdem Sie (und wieder) über Ihre Frage nachgedacht haben: Wenn Sie sich fragen, woher die zusätzlichen 3 GB stammen, hier eine verwandte Frage . Ich persönlich benutze den Faktor x1.5 als Faustregel.

1
t0r0X

Mit jps und jstat können Sie einfach die Details Ihres Java-Programmspeichers nachverfolgen.

Suchen Sie die PID mit dem Befehl jps und verwenden Sie diese Pid, ​​um die Speicherdetails des gewünschten Java-Prozesses mit jstat $pid abzurufen. Führen Sie sie bei Bedarf in einer Schleife aus, und Sie können die gewünschten Speicherdetails genau überwachen.

Eine bash-Implementierung dieser Idee finden Sie auf github

0
amarjeetAnand