Objectif de réglage JVM : Utiliser une empreinte mémoire plus petite pour obtenir un débit plus élevé ou une latence plus faible.
Parfois, il y aura des problèmes JVM, petits et grands, lors du test ou de l'exécution du programme avant la mise en ligne, tels qu'une charge excessive du processeur, un délai de requête, une réduction des tps, etc., et même des fuites de mémoire (utilisées pour chaque garbage collection) Le temps devient de plus en plus long, la fréquence de garbage collection est de plus en plus élevée et les données de garbage nettoyées par chaque garbage collection deviennent de plus en plus petites), un débordement de mémoire provoque un crash du système, donc la JVM doit être réglée pour que le programme puisse fonctionner normalement afin d'obtenir une expérience utilisateur et une efficacité opérationnelle supérieures.
Voici plusieurs indicateurs importants :
Utilisation de la mémoire : la quantité de mémoire requise pour le fonctionnement normal du programme.
Latence : temps de pause du programme dû à la collecte des ordures.
Débit : le rapport entre la durée d'exécution du programme utilisateur et le temps total occupé par le programme utilisateur et le garbage collection.
Bien sûr, tout comme le principe CAP, il est impossible de satisfaire en même temps la petite empreinte mémoire, la faible latence et le débit élevé d'un programme. Les objectifs du programme sont différents. , et la direction à prendre en compte lors du réglage est également différente. Avant le réglage, vous devez combiner le scénario réel, avoir des objectifs d'optimisation clairs, trouver le goulot d'étranglement des performances, optimiser le goulot d'étranglement de manière ciblée et enfin effectuer des tests pour confirmer si. les résultats du réglage répondent aux exigences grâce à divers outils de surveillance Target.
Outil de réglage JVM
(1) Les données sur lesquelles on peut s'appuyer et faire référence pour le réglage incluent les journaux d'exécution du système, les messages d'erreur de pile, les journaux gc, les instantanés de thread, et transferts de tas Stocker des instantanés, etc.
① Journal de fonctionnement du système : Le journal de fonctionnement du système est le journal imprimé dans le code du programme, qui décrit la trajectoire de fonctionnement du système au niveau du code (méthode d'exécution, paramètres d'entrée, valeur de retour, etc.). s'il y a un problème avec le système, le journal de fonctionnement du système est le premier journal à vérifier.
② Informations sur l'erreur de pile : lorsqu'une exception se produit dans le système, le problème peut être initialement localisé en fonction des informations de la pile. Par exemple, sur la base de "java.lang.OutOfMemoryError : espace de tas Java", il peut. être déterminé qu'il s'agit d'un débordement de mémoire de tas ; basé sur "java. "lang.StackOverflowError" peut être considéré comme un débordement de pile ; "java.lang.OutOfMemoryError: PermGen space" peut être considéré comme un débordement de zone de méthode, etc. .
③Journal GC : utilisez -XX:+PrintGCDetails et -Xloggc:/data/jvm/gc.log lorsque le programme commence à enregistrer le processus détaillé de gc pendant l'exécution du programme, ou configurez directement "-verbose : gc" imprime le journal gc sur la console. Grâce au journal gc enregistré, la fréquence, l'heure, etc. de gc dans chaque zone de mémoire peuvent être analysées pour trouver des problèmes et effectuer une optimisation ciblée.
Par exemple, le journal GC suivant :
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]
Il y a 4 journaux GC au total. En regardant la première ligne du journal, "2018-08-02T14:39:11.560-0800". est un format d'heure standard universel UTC précis au niveau de la milliseconde. La configuration du paramètre "-XX:+PrintGCDateStamps" peut imprimer cet horodatage après que le journal gc "10.171" soit le nombre de secondes écoulées entre le début de la JVM et le. apparition de gc. Le "[GC" au début de la première ligne du texte du journal indique que Stop-The-World ne s'est pas produit dans ce GC (le thread utilisateur s'est arrêté). Le "[Full GC" au début de la deuxième ligne du journal). Le texte indique que Stop-The-World s'est produit dans ce GC, donc [GC et [Full GC] n'ont rien à voir avec la nouvelle génération et l'ancienne génération, mais sont liés au type de garbage collector If System.gc. () est appelé directement, [Full GC (System) sera affiché. Les prochains "[PSYoungGen" et "[ParOldGen" représentent la zone où se produit le GC. Le nom spécifique affiché est également lié au garbage collector. Par exemple, "[PSYoungGen" représente ici le collecteur Parallel Scavenge et "[ParOldGen" représente. le collecteur Serial Old, de plus, le collecteur Serial affiche "[DefNew", le collecteur ParNew affiche "[ParNew", etc. Le "30128K->4091K (30208K)" suivant indique qu'après ce gc, l'espace d'utilisation de la mémoire dans cette zone a été réduit de 30128K à 4091K et la taille totale de la mémoire est de 30208K. "51092K->50790K(98816K), 0,0140970 secondes" après la description gc de chaque zone. Après ce garbage collection, l'espace d'utilisation de la mémoire de l'ensemble de la mémoire du tas a été réduit de 51092K à 50790K, et l'espace total de l'ensemble de la mémoire du tas a été réduit. était 98816K. gc a pris 0,0140970 secondes.
④Instantané du fil : comme son nom l'indique, vous pouvez voir l'état du fil à un certain moment en fonction de l'instantané du fil lorsqu'il peut y avoir un délai d'expiration de la demande, une boucle infinie, un blocage, etc. vous pouvez déterminer davantage en fonction de la question de l'instantané du fil. En exécutant la commande "jstack pid" fournie avec la machine virtuelle, vous pouvez vider les informations d'instantané des threads dans le processus en cours. Il existe de nombreux exemples sur Internet pour une utilisation et une analyse plus détaillées. donc je n'entrerai pas dans les détails, publiez un blog pour référence : http://www.cnblogs.com/kongzhongqijing/articles/3630264.html
⑤Instantané de vidage du tas : vous pouvez utiliser "-XX : +HeapDumpOnOutOfMemory" et "-XX:HeapDumpPath=/data/jvm/dumpfile.hprof", lorsqu'un dépassement de mémoire se produit dans le programme, l'instantané de mémoire à ce moment-là est vidé sous la forme d'un fichier (vous pouvez également utiliser directement le jmap pour vider la mémoire à tout moment lorsque le programme est en cours d'exécution) Snapshot), et analyser l'utilisation de la mémoire à ce moment-là par la suite.
(2) Outil de réglage JVM
①用 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) Expérience de réglage JVM
En termes de configuration JVM, en général, vous pouvez d'abord utiliser la configuration par défaut (certains paramètres initiaux de base peuvent garantir que les applications générales fonctionnent de manière plus stable pendant le test). en fonction de l'état de fonctionnement du système (concurrence de session, durée de session, etc.), combiné aux journaux gc, à la surveillance de la mémoire, aux garbage collector utilisés, etc., effectuez des ajustements raisonnables. Lorsque la mémoire d'ancienne génération est trop petite, cela peut provoquer des saturations fréquentes. GC. Lorsque la mémoire est trop grande, le temps Full GC sera extrêmement long.
Alors, quelle est la configuration JVM la plus appropriée, comme la nouvelle génération et l'ancienne génération ? La réponse n'est pas nécessairement le processus de recherche de la réponse. Lorsque la mémoire physique est certaine, plus le paramètre de nouvelle génération est grand, plus l'ancienne génération est petite, plus la fréquence Full GC est élevée, mais plus le temps Full GC est court ; au contraire, le paramètre de nouvelle génération Plus il est petit, plus l'ancienne génération est grande et plus la fréquence de Full GC est basse, mais plus chaque Full GC consomme de temps. Les suggestions sont les suivantes :
Définissez les valeurs de -Xms et -Xmx pour qu'elles soient égales. La taille du tas est par défaut la taille spécifiée par -Xms lorsque le tas libre par défaut. la mémoire est inférieure à 40 %, la JVM étendra le tas à la taille spécifiée par -Xmx ; lorsque la mémoire du tas libre est supérieure à 70 %, la JVM réduira le tas à la taille spécifiée par -Xms. Si la demande de mémoire ne peut pas être satisfaite après Full GC, elle sera ajustée dynamiquement. Cette étape est relativement gourmande en ressources.
La nouvelle génération doit être définie aussi grande que possible pour permettre aux objets de survivre dans la nouvelle génération pendant une période plus longue. Chaque GC mineur doit collecter autant d'objets poubelles que possible. empêcher ou retarder l’entrée des objets dans l’ancienne génération afin de réduire la fréquence des GC complets dans votre application.
Si vous utilisez le collecteur CMS de l'ancienne génération, la nouvelle génération n'a pas besoin d'être trop grande, car la vitesse de collecte parallèle du CMS est également très rapide, et le marquage simultané et les phases de suppression simultanées du processus de collecte prennent du temps. Peuvent être exécutées simultanément avec les threads utilisateur.
Définition de la taille de la zone de méthode. Avant la version 1.6, vous devez prendre en compte les constantes, les variables statiques, etc. qui sont ajoutées dynamiquement lorsque le système est en cours d'exécution. car il peut presque accueillir les informations de classe chargées dynamiquement au démarrage et plus tard.
En termes d'implémentation du code, les problèmes de performances tels que l'attente du programme et les fuites de mémoire ne sont pas seulement des problèmes possibles avec la configuration de la JVM, mais ont également beaucoup à voir avec l'implémentation du code :
Évitez de créer des objets et des tableaux trop grands : Les objets ou tableaux trop grands entreront directement dans l'ancienne génération lorsque la nouvelle génération n'a pas assez d'espace pour les accueillir. S'il s'agit de gros objets de courte durée, Le GC complet commencera tôt.
Évitez de charger une grande quantité de données en même temps, par exemple en récupérant une grande quantité de données de la base de données en même temps ou en lisant un grand nombre d'enregistrements à partir d'Excel en même temps. temps. Vous pouvez lire par lots et effacer les références dès que possible.
Lorsqu'il y a des références à des objets de la collection, les références de la collection doivent être effacées dans les plus brefs délais après l'utilisation de ces objets. Ces objets inutiles doivent être recyclés dans les plus brefs délais. pour éviter d'entrer dans la vieillesse.
Vous pouvez utiliser des références logicielles et des références faibles dans des scénarios appropriés (tels que l'implémentation de la mise en cache), par exemple en utilisant des références logicielles pour allouer des instances d'ObjectA : SoftReference objectA=new SoftReference(); génération de mémoire Avant le débordement, l'objetA sera inclus dans la plage de recyclage pour le recyclage secondaire. S'il n'y a pas assez de mémoire pour ce recyclage, une exception de débordement de mémoire sera levée.
Évitez de créer une boucle infinie. Après qu'une boucle infinie se produit, un grand nombre d'instances peuvent être générées à plusieurs reprises dans le corps de la boucle, provoquant un remplissage rapide de l'espace mémoire.
Essayez d'éviter d'attendre de longues périodes pour des ressources externes (bases de données, réseaux, ressources d'équipement, etc.), de raccourcir le cycle de vie des objets et d'éviter d'entrer dans la vieillesse. Si les résultats ne peuvent pas être renvoyés à temps, vous pouvez les utiliser de manière appropriée méthodes de traitement asynchrone, etc.
(4) Cas d'enregistrement de dépannage JVM
Dépannage du service JVM https://blog.csdn.net/jacin1/article/details/44837595
Dépannage inoubliable du processus Full GC fréquent http://caogen81.iteye.com/blog/1513345
Dépannage fréquent du Full GC en ligne https://blog.csdn.net/wilsonpeng3 /article/details/70064336 /
[JVM] Dépannage d'application en ligne https://www.cnblogs.com/Dhouse/p/7839810.html
Un dépannage de FullGC dans le processus JVM http://iamzhongyong.iteye .com/blog/1830265
Cas de dépannage de CPU excessif causé par un débordement de mémoire JVM https://blog.csdn.net/nielinqi520/article/details/78455614
Dépannage d'une fuite de mémoire Java cas https://blog.csdn.net/aasgis6u/article/details/54928744
(5) Référence des paramètres JVM communs :
参数 | 说明 | 实例 |
---|---|---|
-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情况。 |
Cet article provient du site Web php chinois, colonne tutoriel Java, bienvenue pour apprendre !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!