Ziel der JVM-Optimierung: Verwenden Sie einen kleineren Speicherbedarf, um einen höheren Durchsatz oder eine geringere Latenz zu erzielen.
Manchmal kommt es beim Testen oder Ausführen des Programms vor dem Online-Gehen zu großen und kleinen JVM-Problemen, wie z. B. übermäßige CPU-Last, Anforderungsverzögerung, reduzierte TPS usw. und sogar Speicherlecks (verwendet). (für jede Garbage Collection) Die Zeit wird immer länger, die Häufigkeit der Garbage Collection wird immer höher und die von jeder Garbage Collection bereinigten Mülldaten werden immer kleiner.) Ein Speicherüberlauf führt zum Absturz des Systems Die JVM muss so optimiert werden, dass das Programm normal ausgeführt werden kann, um eine höhere Benutzererfahrung und Betriebseffizienz zu erreichen.
Hier sind einige wichtige Indikatoren:
Speichernutzung: die Menge an Speicher, die für den normalen Betrieb des Programms erforderlich ist.
Latenz: Programmpausenzeit aufgrund der Speicherbereinigung.
Durchsatz: Das Verhältnis der Laufzeit des Benutzerprogramms zur Gesamtzeit, die das Benutzerprogramm und die Speicherbereinigung in Anspruch nehmen.
Natürlich ist es genau wie beim CAP-Prinzip unmöglich, gleichzeitig den geringen Speicherbedarf, die geringe Latenz und den hohen Durchsatz eines Programms zu erfüllen. Die Ziele des Programms sind unterschiedlich , und die Richtung, die beim Tuning berücksichtigt werden muss Es ist auch anders. Vor dem Tuning müssen Sie das tatsächliche Szenario kombinieren, klare Optimierungsziele haben, den Leistungsengpass finden, den Engpass gezielt optimieren und schließlich Tests durchführen, um zu bestätigen, ob Die Tuning-Ergebnisse erfüllen die Anforderungen durch verschiedene Überwachungstools.
JVM-Tuning-Tool
(1) Zu den Daten, auf die sich das Tuning verlassen und auf die es sich beziehen kann, gehören Systemlaufprotokolle, Stack-Fehlermeldungen, GC-Protokolle, Thread-Snapshots usw Heap-Transfers, Speichern von Snapshots usw.
① Systembetriebsprotokoll: Das Systembetriebsprotokoll ist das im Programmcode gedruckte Protokoll, das den Systembetriebsverlauf auf Codeebene beschreibt (Ausführungsmethode, Eingabeparameter, Rückgabewert usw.). Wenn ein Problem mit dem System vorliegt, ist das Systembetriebsprotokoll das erste zu überprüfende Protokoll.
② Stack-Fehlerinformationen: Wenn im System eine Ausnahme auftritt, kann das Problem zunächst anhand der Stack-Informationen lokalisiert werden. Beispielsweise anhand von „java.lang.OutOfMemoryError: Java-Heap-Speicherplatz“. basierend auf „java. „lang.StackOverflowError“ kann als Stapelüberlauf beurteilt werden; „java.lang.OutOfMemoryError: PermGen space“ kann als Methodenbereichsüberlauf usw. beurteilt werden .
③GC-Protokoll: Verwenden Sie -XX:+PrintGCDetails und -Xloggc:/data/jvm/gc.log, wenn das Programm startet, um den detaillierten Prozess von gc aufzuzeichnen, während das Programm ausgeführt wird, oder konfigurieren Sie „-verbose“ direkt Der Parameter „: gc“ gibt das GC-Protokoll an die Konsole aus. Mithilfe des aufgezeichneten GC-Protokolls können Häufigkeit, Zeit usw. von GC in jedem Speicherbereich analysiert werden, um Probleme zu finden und eine gezielte Optimierung durchzuführen.
Zum Beispiel das folgende GC-Protokoll:
2018-08-02T14:39:11.560-0800: 10.171: [GC [PSYoungGen: 30128K->4091K(30208K)] 51092K->50790K(98816K), 0.0140970 secs] [Times: user=0.02 sys=0.03, real=0.01 secs] 2018-08-02T14:39:11.574-0800: 10.185: [Full GC [PSYoungGen: 4091K->0K(30208K)] [ParOldGen: 46698K->50669K(68608K)] 50790K->50669K(98816K) [PSPermGen: 2635K->2634K(21504K)], 0.0160030 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 2018-08-02T14:39:14.045-0800: 12.656: [GC [PSYoungGen: 14097K->4064K(30208K)] 64766K->64536K(98816K), 0.0117690 secs] [Times: user=0.02 sys=0.01, real=0.01 secs] 2018-08-02T14:39:14.057-0800: 12.668: [Full GC [PSYoungGen: 4064K->0K(30208K)] [ParOldGen: 60471K->401K(68608K)] 64536K->401K(98816K) [PSPermGen: 2634K->2634K(21504K)], 0.0102020 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
Es gibt insgesamt 4 GC-Protokolle, wenn man sich die erste Zeile des Protokolls ansieht: „2018-08-02T14:39:11.560-0800“. ist ein genaues UTC-Standardzeitformat auf Millisekundenebene. Durch Konfigurieren des Parameters „-XX:+PrintGCDateStamps“ kann dieser Zeitstempel nach dem GC-Protokoll ausgedruckt werden. „10.171“ ist die Anzahl der Sekunden, die vom Start der JVM bis zum Auftreten von GC. Das „[GC“ am Anfang der ersten Zeile des Protokolltexts zeigt an, dass Stop-The-World in diesem GC nicht aufgetreten ist (der Benutzerthread wurde angehalten). Das „[Full GC“ am Anfang der zweiten Zeile des Protokolls Der Text weist darauf hin, dass Stop-The-World in dieser GC-Welt aufgetreten ist, daher haben [GC und [Full GC] nichts mit der neuen Generation und der alten Generation zu tun, sondern hängen mit der Art des Garbage Collectors zusammen, wenn Sie System aufrufen .gc() direkt, [Vollständiger GC(System) wird angezeigt. Die nächsten „[PSYoungGen““ und „[ParOldGen““ stellen den Bereich dar, in dem GC auftritt. Der angezeigte spezifische Name bezieht sich beispielsweise auf den „Parallel Scavenge Collector“. Der Serial Old-Sammler zeigt außerdem „[DefNew““ an, der ParNew-Kollektor zeigt „[ParNew““ usw. an. Das folgende „30128K->4091K (30208K)“ zeigt an, dass nach diesem gc der Speichernutzungsraum in diesem Bereich von 30128K auf 4091K reduziert wurde und die Gesamtspeichergröße 30208K beträgt. „51092K->50790K(98816K), 0,0140970 Sekunden“ nach der GC-Beschreibung jedes Bereichs. Nach dieser Speicherbereinigung wurde der Speichernutzungsraum des gesamten Heap-Speichers von 51092K auf 50790K reduziert und der Gesamtspeicherplatz des gesamten Heap-Speichers war 98816K. GC dauerte 0,0140970 Sekunden.
④Thread-Snapshot: Wie der Name schon sagt, können Sie den Status des Threads zu einem bestimmten Zeitpunkt anhand des Thread-Snapshots sehen, wenn im System ein Anforderungszeitlimit, eine Endlosschleife, ein Deadlock usw. vorliegt. Sie können dies anhand der Thread-Snapshot-Frage weiter bestimmen. Durch Ausführen des mit der virtuellen Maschine gelieferten Befehls „jstack pid“ können Sie die Snapshot-Informationen der Threads im aktuellen Prozess sichern. Es gibt viele Beispiele im Internet für eine detailliertere Verwendung und Analyse. Dieser Artikel ist bereits sehr lang. Deshalb werde ich nicht auf Details eingehen. Veröffentlichen Sie einen Blog als Referenz: http://www.cnblogs.com/kongzhongqijing/articles/3630264.html
⑤Heap-Dump-Snapshot: Sie können „-XX“ verwenden: +HeapDumpOnOutOfMemory“ und „-XX:HeapDumpPath=/data/jvm/dumpfile.hprof“, wenn im Programm ein Speicherüberlauf auftritt, wird der Speicher-Snapshot zu diesem Zeitpunkt in Form einer Datei ausgegeben (Sie können dies auch direkt verwenden jmap-Befehl, um den Speicher jederzeit zu sichern, wenn das Programm ausgeführt wird (Snapshot) und anschließend die Speichernutzung zu diesem Zeitpunkt zu analysieren.
(2) JVM-Tuning-Tool
①用 jps(JVM process Status)可以查看虚拟机启动的所有进程、执行主类的全名、JVM启动参数,比如当执行了JPSTest类中的main方法后(main方法持续执行),执行 jps -l可看到下面的JPSTest类的pid为31354,加上-v参数还可以看到JVM启动参数。
3265 32914 sun.tools.jps.Jps 31353 org.jetbrains.jps.cmdline.Launcher 31354 com.danny.test.code.jvm.JPSTest 380
②用jstat(JVM Statistics Monitoring Tool)监视虚拟机信息
jstat -gc pid 500 10 :每500毫秒打印一次Java堆状况(各个区的容量、使用容量、gc时间等信息),打印10次
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 11264.0 11264.0 11202.7 0.0 11776.0 1154.3 68608.0 36238.7 - - - - 14 0.077 7 0.049 0.126 11264.0 11264.0 11202.7 0.0 11776.0 4037.0 68608.0 36238.7 - - - - 14 0.077 7 0.049 0.126 11264.0 11264.0 11202.7 0.0 11776.0 6604.5 68608.0 36238.7 - - - - 14 0.077 7 0.049 0.126 11264.0 11264.0 11202.7 0.0 11776.0 9487.2 68608.0 36238.7 - - - - 14 0.077 7 0.049 0.126 11264.0 11264.0 0.0 0.0 11776.0 258.1 68608.0 58983.4 - - - - 15 0.082 8 0.059 0.141 11264.0 11264.0 0.0 0.0 11776.0 3076.8 68608.0 58983.4 - - - - 15 0.082 8 0.059 0.141 11264.0 11264.0 0.0 0.0 11776.0 0.0 68608.0 390.0 - - - - 16 0.084 9 0.066 0.149 11264.0 11264.0 0.0 0.0 11776.0 0.0 68608.0 390.0 - - - - 16 0.084 9 0.066 0.149 11264.0 11264.0 0.0 0.0 11776.0 258.1 68608.0 390.0 - - - - 16 0.084 9 0.066 0.149 11264.0 11264.0 0.0 0.0 11776.0 3012.8 68608.0 390.0 - - - - 16 0.084 9 0.066 0.149
jstat还可以以其他角度监视各区内存大小、监视类装载信息等,具体可以google jstat的详细用法。
③用jmap(Memory Map for Java)查看堆内存信息
执行jmap -histo pid可以打印出当前堆中所有每个类的实例数量和内存占用,如下,class name是每个类的类名([B是byte类型,[C是char类型,[I是int类型),bytes是这个类的所有示例占用内存大小,instances是这个类的实例数量:
num #instances #bytes class name ---------------------------------------------- 1: 2291 29274080 [B 2: 15252 1961040 <methodKlass> 3: 15252 1871400 <constMethodKlass> 4: 18038 721520 java.util.TreeMap$Entry 5: 6182 530088 [C 6: 11391 273384 java.lang.Long 7: 5576 267648 java.util.TreeMap 8: 50 155872 [I 9: 6124 146976 java.lang.String 10: 3330 133200 java.util.LinkedHashMap$Entry 11: 5544 133056 javax.management.openmbean.CompositeDataSupport
执行 jmap -dump 可以转储堆内存快照到指定文件,比如执行 jmap -dump:format=b,file=/data/jvm/dumpfile_jmap.hprof 3361 可以把当前堆内存的快照转储到dumpfile_jmap.hprof文件中,然后可以对内存快照进行分析。
④利用jconsole、jvisualvm分析内存信息(各个区如Eden、Survivor、Old等内存变化情况),如果查看的是远程服务器的JVM,程序启动需要加上如下参数:
"-Dcom.sun.management.jmxremote=true" "-Djava.rmi.server.hostname=12.34.56.78" "-Dcom.sun.management.jmxremote.port=18181" "-Dcom.sun.management.jmxremote.authenticate=false" "-Dcom.sun.management.jmxremote.ssl=false"
下图是jconsole界面,概览选项可以观测堆内存使用量、线程数、类加载数和CPU占用率;内存选项可以查看堆中各个区域的内存使用量和左下角的详细描述(内存大小、GC情况等);线程选项可以查看当前JVM加载的线程,查看每个线程的堆栈信息,还可以检测死锁;VM概要描述了虚拟机的各种详细参数。(jconsole功能演示)
下图是jvisualvm的界面,功能比jconsole略丰富一些,不过大部分功能都需要安装插件。概述跟jconsole的VM概要差不多,描述的是jvm的详细参数和程序启动参数;监视展示的和jconsole的概览界面差不多(CPU、堆/方法区、类加载、线程);线程和jconsole的线程界面差不多;抽样器可以展示当前占用内存的类的排行榜及其实例的个数;Visual GC可以更丰富地展示当前各个区域的内存占用大小及历史信息(下图)。(jvisualvm功能演示)
⑤分析堆转储快照
前面说到配置了 “-XX:+HeapDumpOnOutOfMemory” 参数可以在程序发生内存溢出时dump出当前的内存快照,也可以用jmap命令随时dump出当时内存状态的快照信息,dump的内存快照一般是以.hprof为后缀的二进制格式文件。
可以直接用 jhat(JVM Heap Analysis Tool) 命令来分析内存快照,它的本质实际上内嵌了一个微型的服务器,可以通过浏览器来分析对应的内存快照,比如执行 jhat -port 9810 -J-Xmx4G /data/jvm/dumpfile_jmap.hprof 表示以9810端口启动 jhat 内嵌的服务器:
Reading from /Users/dannyhoo/data/jvm/dumpfile_jmap.hprof... Dump file created Fri Aug 03 15:48:27 CST 2018 Snapshot read, resolving... Resolving 276472 objects... Chasing references, expect 55 dots....................................................... Eliminating duplicate references....................................................... Snapshot resolved. Started HTTP server on port 9810 Server is ready.
在控制台可以看到服务器启动了,访问 http://127.0.0.1:9810/ 可以看到对快照中的每个类进行分析的结果(界面略low),下图是我随便选择了一个类的信息,有这个类的父类,加载这个类的类加载器和占用的空间大小,下面还有这个类的每个实例(References)及其内存地址和大小,点进去会显示这个实例的一些成员变量等信息:
jvisualvm也可以分析内存快照,在jvisualvm菜单的“文件”-“装入”,选择堆内存快照,快照中的信息就以图形界面展示出来了,如下,主要可以查看每个类占用的空间、实例的数量和实例的详情等:
还有很多分析内存快照的第三方工具,比如eclipse mat,它比jvisualvm功能更专业,出了查看每个类及对应实例占用的空间、数量,还可以查询对象之间的调用链,可以查看某个实例到GC Root之间的链,等等。可以在eclipse中安装mat插件,也可以下载独立的版本(http://www.eclipse.org/mat/downloads.php ),我在mac上安装后运行起来老卡死~下面是在windows上的截图(MAT功能演示):
(3) JVM-Tuning-Erfahrung
In Bezug auf die JVM-Konfiguration können Sie im Allgemeinen zuerst die Standardkonfiguration verwenden (einige grundlegende Anfangsparameter können sicherstellen, dass allgemeine Anwendungen während des Tests stabiler laufen). Abhängig vom Betriebsstatus des Systems (Sitzungsgleichzeitigkeit, Sitzungszeit usw.) können in Kombination mit GC-Protokollen, Speicherüberwachung, verwendeten Garbage Collectors usw. angemessene Anpassungen vorgenommen werden. Wenn der Speicher der alten Generation zu klein ist, kann dies zu häufigen Volllastungen führen GC. Wenn der Speicher zu groß ist, ist die vollständige GC-Zeit extrem lang.
Was ist also die am besten geeignete JVM-Konfiguration, z. B. die neue Generation und die alte Generation? Die Antwort ist nicht unbedingt der Prozess, die Antwort zu finden. Wenn der physische Speicher sicher ist, gilt: Je größer die neue Generation, desto kleiner die alte Generation, desto höher ist die vollständige GC-Zeit. Im Gegenteil, die Einstellung der neuen Generation: Je kleiner sie ist, desto größer ist die alte Generation und desto geringer ist die Häufigkeit des vollständigen GC, aber desto mehr Zeit verbraucht jeder vollständige GC. Die Vorschläge lauten wie folgt:
Die Werte von -Xms und -Xmx sind standardmäßig auf die durch -Xms angegebene Größe eingestellt Der Heap-Speicher beträgt weniger als 40 %, die JVM erweitert den Heap auf die durch -Xmx angegebene Größe. Wenn der freie Heap-Speicher größer als 70 % ist, reduziert die JVM den Heap auf die durch -Xms angegebene Größe. Wenn der Speicherbedarf nach der vollständigen GC nicht gedeckt werden kann, wird er dynamisch angepasst. Diese Phase ist relativ ressourcenintensiv.
Die neue Generation sollte so groß wie möglich sein, damit Objekte in der neuen Generation über einen längeren Zeitraum überleben können. Jeder Minor GC muss so viele Müllobjekte wie möglich sammeln Verhindern oder verzögern Sie den Eintritt von Objekten in die alte Generation. Möglichkeit, die Häufigkeit von Full GC in Ihrer Anwendung zu reduzieren.
Wenn Sie den CMS-Kollektor der alten Generation verwenden, muss die neue Generation nicht zu groß sein, da die parallele Erfassungsgeschwindigkeit von CMS und die gleichzeitige Markierung ebenfalls sehr hoch sind und gleichzeitige Löschphasen des Erfassungsprozesses sind zeitaufwändig. Kann gleichzeitig mit Benutzer-Threads ausgeführt werden.
Festlegen der Größe des Methodenbereichs. Vor 1.6 müssen Sie die Konstanten, statischen Variablen usw. berücksichtigen, die dynamisch hinzugefügt werden, wenn das System ausgeführt wird muss in der Lage sein, die Klasseninformationen aufzunehmen, die beim Start und später dynamisch geladen werden.
In Bezug auf die Code-Implementierung sind Leistungsprobleme wie Programmwartezeiten und Speicherlecks nicht nur mögliche Probleme bei der JVM-Konfiguration, sondern haben auch viel mit der Code-Implementierung zu tun:
Vermeiden Sie die Erstellung übermäßig großer Objekte und Arrays: Übermäßig große Objekte oder Arrays gelangen direkt in die alte Generation, wenn die neue Generation nicht über genügend Platz verfügt, um sie aufzunehmen. Wenn es sich um kurzlebige große Objekte handelt, Die vollständige GC beginnt früh.
Vermeiden Sie das gleichzeitige Laden einer großen Datenmenge, z. B. das gleichzeitige Abrufen einer großen Datenmenge aus der Datenbank oder das gleichzeitige Lesen einer großen Anzahl von Datensätzen aus Excel Sie können die Referenzen schnellstmöglich einlesen und löschen.
Wenn Verweise auf Objekte in der Sammlung vorhanden sind, müssen die Verweise in der Sammlung so schnell wie möglich gelöscht werden, nachdem diese Objekte verwendet wurden. Diese nutzlosen Objekte müssen so schnell wie möglich recycelt werden um den Eintritt ins hohe Alter zu vermeiden.
Sie können Soft-Referenzen und schwache Referenzen in geeigneten Szenarien verwenden (z. B. bei der Implementierung von Caching), z. B. indem Sie Soft-Referenzen verwenden, um Instanzen von ObjectA zuzuweisen: SoftReference objectA=new SoftReference(); Speichergenerierung Vor dem Überlauf wird ObjektA in den Recyclingbereich für das sekundäre Recycling aufgenommen. Wenn für dieses Recycling nicht genügend Speicher vorhanden ist, wird eine Speicherüberlaufausnahme ausgelöst.
Vermeiden Sie das Erstellen einer Endlosschleife. Nach dem Auftreten einer Endlosschleife kann es passieren, dass eine große Anzahl von Instanzen im Schleifenkörper wiederholt generiert wird, wodurch der Speicherplatz schnell voll wird.
Versuchen Sie, lange Wartezeiten auf externe Ressourcen (Datenbanken, Netzwerke, Geräteressourcen usw.) zu vermeiden, verkürzen Sie den Lebenszyklus von Objekten und vermeiden Sie den Eintritt ins hohe Alter. Wenn die Ergebnisse nicht rechtzeitig zurückgegeben werden können, können Sie sie entsprechend verwenden asynchrone Verarbeitungsmethoden usw.
(4) JVM-Fehlerbehebungsdatensatzfall
JVM-Dienst-Fehlerbehebung https://blog.csdn.net/jacin1/article/details/44837595
Unvergessliche Fehlerbehebung bei häufigen Full-GC-Prozessen http://caogen81.iteye.com/blog/1513345
Häufige Fehlerbehebung bei Online-Full-GC https://blog.csdn.net/wilsonpeng3 /article/details/70064336 /
[JVM] Fehlerbehebung bei Online-Anwendungen https://www.cnblogs.com/Dhouse/p/7839810.html
Eine Fehlerbehebung von FullGC im JVM-Prozess http://iamzhongyong.iteye .com/blog/1830265
Fehlerbehebung bei übermäßiger CPU-Auslastung durch JVM-Speicherüberlauf https://blog.csdn.net/nielinqi520/article/details/78455614
Eine Fehlerbehebung bei Java-Speicherlecks Fall https://blog.csdn.net/aasgis6u/article/details/54928744
(5) Allgemeine JVM-Parameterreferenz:
参数 | 说明 | 实例 |
---|---|---|
-Xms | 初始堆大小,默认物理内存的1/64 | -Xms512M |
-Xmx | 最大堆大小,默认物理内存的1/4 | -Xms2G |
-Xmn | 新生代内存大小,官方推荐为整个堆的3/8 | -Xmn512M |
-Xss | 线程堆栈大小,jdk1.5及之后默认1M,之前默认256k | -Xss512k |
-XX:NewRatio=n | 设置新生代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 | -XX:NewRatio=3 |
-XX:SurvivorRatio=n | 年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1:1,一个Survivor区占整个年轻代的1/8 | -XX:SurvivorRatio=8 |
-XX:PermSize=n | 永久代初始值,默认为物理内存的1/64 | -XX:PermSize=128M |
-XX:MaxPermSize=n | 永久代最大值,默认为物理内存的1/4 | -XX:MaxPermSize=256M |
-verbose:class | 在控制台打印类加载信息 | |
-verbose:gc | 在控制台打印垃圾回收日志 | |
-XX:+PrintGC | 打印GC日志,内容简单 | |
-XX:+PrintGCDetails | 打印GC日志,内容详细 | |
-XX:+PrintGCDateStamps | 在GC日志中添加时间戳 | |
-Xloggc:filename | 指定gc日志路径 | -Xloggc:/data/jvm/gc.log |
-XX:+UseSerialGC | 年轻代设置串行收集器Serial | |
-XX:+UseParallelGC | 年轻代设置并行收集器Parallel Scavenge | |
-XX:ParallelGCThreads=n | 设置Parallel Scavenge收集时使用的CPU数。并行收集线程数。 | -XX:ParallelGCThreads=4 |
-XX:MaxGCPauseMillis=n | 设置Parallel Scavenge回收的最大时间(毫秒) | -XX:MaxGCPauseMillis=100 |
-XX:GCTimeRatio=n | 设置Parallel Scavenge垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) | -XX:GCTimeRatio=19 |
-XX:+UseParallelOldGC | 设置老年代为并行收集器ParallelOld收集器 | |
-XX:+UseConcMarkSweepGC | 设置老年代并发收集器CMS | |
-XX:+CMSIncrementalMode | 设置CMS收集器为增量模式,适用于单CPU情况。 |
Dieser Artikel stammt von der chinesischen PHP-Website, Spalte Java-Tutorial, willkommen zum Lernen!
Das obige ist der detaillierte Inhalt vonWarum benötigt JVM eine Leistungsoptimierung?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!