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:
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.
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.
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.
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 .....
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).
PPPSWe 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:
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.
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