JVM チューニング目標: より高いスループットまたはより低いレイテンシを得るには、より小さいメモリ フットプリントを使用します。
オンラインになる前のプログラムのテスト中または実行中に、過剰な CPU 負荷、リクエストの遅延、TPS の低下など、さらにはメモリ リーク (使用済み) など、大小さまざまな JVM の問題が発生することがあります。ガベージ コレクションごとに)、時間はますます長くなり、ガベージ コレクションの頻度はますます高くなり、各ガベージ コレクションによってクリーンアップされるガベージ データはますます小さくなります)、メモリ オーバーフローによりシステムがクラッシュするため、より高いユーザー エクスペリエンスと操作効率を実現するには、プログラムが正常に実行できるように JVM を調整する必要があります。
ここにいくつかの重要な指標があります:
メモリ使用量: プログラムの通常の動作に必要なメモリの量。
遅延: ガベージ コレクションによるプログラムの一時停止時間。
スループット: ユーザー プログラムとガベージ コレクションによって占められた合計時間に対するユーザー プログラムの実行時間の比率。
もちろん、CAP 原則と同様に、プログラムの小さなメモリ使用量、低レイテンシ、および高スループットを同時に満たすことは不可能です。プログラムの目標は異なります。チューニングの際に考慮すべき方向性も異なります チューニングの前に、実際のシナリオを組み合わせ、明確な最適化目標を持ち、パフォーマンスのボトルネックを見つけ、ターゲットを絞った方法でボトルネックを最適化し、最後にチューニングの結果が得られるかどうかを確認するテストを行う必要がありますさまざまな監視ツールを通じて要件を満たします。
JVM チューニング ツール
(1) チューニングが依存および参照できるデータには、システム実行ログ、スタック エラー メッセージ、GC ログ、スレッド スナップショット、およびヒープ転送 スナップショットなどの保存
① システム動作ログ: システム動作ログは、プログラムコード内に出力されるログであり、システム動作の軌跡をコードレベル (実行メソッド、入力パラメータ、戻り値など) で記述します。システムに問題がある場合、最初に確認すべきログはシステム動作ログです。
② スタック エラー情報: システムで例外が発生した場合、スタック情報に基づいて問題を最初に特定できます。たとえば、「java.lang.OutOfMemoryError: Java heap space」に基づいて、問題を特定できます。 「java.lang.StackOverflowError」はスタックオーバーフローと判断、「java.lang.OutOfMemoryError: PermGen space」はメソッド領域オーバーフローと判断、など。
③GC ログ: プログラム実行時に gc の詳細なプロセスを記録するには、プログラムの開始時に -XX: PrintGCDetails と -Xloggc:/data/jvm/gc.log を使用するか、または直接「-verbose:」を設定します。 gc "パラメータは、gc ログをコンソールに出力します。記録された gc ログを通じて、各メモリ領域の gc の頻度、時間などを分析して、問題を発見し、目的の最適化を実行できます。
たとえば、次の GC ログ:
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]
合計 4 つの GC ログがあります。ログの最初の行「2018-08-02T14:39:11.560-0800」を見ると、正確です。ミリ秒レベルの UTC 世界標準時刻形式です。パラメータ「-XX: PrintGCDateStamps」を設定すると、GC ログの後にこのタイムスタンプを出力できます。「10.171」は、JVM の開始から JVM が開始されるまでに経過した秒数です。 GCの発生。ログ テキストの 1 行目の先頭にある "[GC" は、この GC で Stop-The-World が発生しなかった (ユーザー スレッドが一時停止した) ことを示します。ログ テキストの 2 行目の先頭にある "[Full GC" は、テキストは、この GC で Stop-The-World が発生したことを示します。World したがって、[GC と [Full GC] は新しい世代と古い世代とは関係ありませんが、ガベージ コレクターのタイプに関係します。System を呼び出す場合.gc() を直接実行すると、[Full GC(System) が表示されます。次の「[PSYoungGen」と「[ParOldGen」は GC が発生する領域を表します。表示される特定の名前はガベージ コレクターにも関連します。たとえば、ここでの「[PSYoungGen」は Parallel Scavenge コレクターを表し、「[ParOldGen」は Parallel Scavenge コレクターを表します)。 Serial Old コレクション、コレクター、さらに Serial コレクターは「[DefNew」、ParNew コレクターは「[ParNew」などと表示されます。次の「30128K->4091K (30208K)」は、今回の gc 以降、この領域のメモリ使用量が 30128K から 4091K に削減され、合計メモリ サイズが 30208K になったことを示しています。各領域の gc 記述後「51092K->50790K(98816K), 0.0140970 秒」 このガベージコレクションにより、ヒープメモリ全体のメモリ使用量が 51092K から 50790K に減少し、ヒープメモリ全体のメモリ使用量の合計が減少しました98816K でした。gc には 0.0140970 秒かかりました。
④スレッドスナップショット: その名の通り、システム内でリクエストタイムアウトや無限ループ、デッドロックなどが発生した際に、スレッドスナップショットによりその時点でのスレッドの状態を確認することができます。スレッドのスナップショットに基づいてさらに判断できます。仮想マシンに付属の「jstack pid」コマンドを実行すると、現在のプロセスのスレッドのスナップショット情報をダンプできます。より詳細な使用方法と分析については、インターネット上に多くの例があります。この記事はすでに非常に長いので、詳細については説明しません。参照用にブログを投稿してください: http://www.cnblogs.com/kongzhongqijing/articles/3630264.html
⑤ ヒープ ダンプ スナップショット: 「-XX」を使用できます。 : HeapDumpOnOutOfMemory」および「 -XX:HeapDumpPath=/data/jvm/dumpfile.hprof」では、プログラム内でメモリオーバーフローが発生した場合、その時点のメモリスナップショットがファイル形式でダンプされます(直接使用することもできます)。 jmap コマンドを使用して、プログラムの実行中にいつでもメモリ スナップショットをダンプし、その後その時点でのメモリ使用量を分析します。
(2)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) JVM チューニングの経験
JVM 構成に関しては、一般に、最初にデフォルト構成を使用できます (いくつかの基本的な初期パラメーターを使用すると、一般的なアプリケーションをより安定して実行できます)。システムに応じて動作ステータス (セッションの同時実行数、セッション時間など) を gc ログ、メモリ監視、使用するガベージ コレクタなどと組み合わせて適切に調整します。旧世代のメモリが小さすぎると、頻繁なフル メモリが発生する可能性があります。 GC. メモリが大きすぎると、Full GC 時間が非常に長くなります。
では、新世代と旧世代など、最も適切な JVM 構成は何でしょうか?答えは必ずしもありません。チューニングは答えを見つけるプロセスです。物理メモリが確実である場合、新しい世代の設定が大きいほど、古い世代の設定が小さいほど、フル GC の頻度は高くなりますが、フル GC 時間は短くなります。逆に、新しい世代の設定が小さいほど、古い世代が大きくなり、フル GC の頻度は低くなりますが、各フル GC に消費される時間は長くなります。提案は次のとおりです:
-Xms と -Xmx の値は同じになるように設定されます。ヒープ サイズのデフォルトは、-Xms で指定されたサイズになります。デフォルトの空きサイズの場合、ヒープ メモリが 40% 未満の場合、JVM はヒープを -Xmx で指定されたサイズまで拡張します。空きヒープ メモリが 70% を超える場合、JVM はヒープを -Xms で指定されたサイズまで削減します。フル GC 後にメモリ需要を満たすことができない場合は、動的に調整されますが、この段階では比較的リソースを消費します。
オブジェクトが新しい世代で長期間存続できるように、新しい世代はできるだけ大きく設定する必要があります。各マイナー GC は、できるだけ多くのガベージ オブジェクトを収集して、オブジェクトが古い世代に入るのを防ぐか遅らせることで、アプリケーションで発生するフル GC の頻度を減らすことができます。
古い世代で CMS コレクターを使用している場合、CMS の並列収集速度も非常に高速であり、同時マーキングも高速であるため、新世代をあまり大きくする必要はありません。収集プロセスの同時クリアフェーズは時間がかかりますが、ユーザースレッドと同時に実行できます。
メソッド領域のサイズの設定 1.6 より前では、システム実行時に動的に追加される定数や静的変数などを考慮する必要がありましたが、1.7 では、起動時以降に動的にロードされるクラス情報をほぼ収容できるためです。
コードの実装に関しては、プログラムの待機やメモリ リークなどのパフォーマンスの問題は、JVM 構成の問題だけでなく、コードの実装にも大きく関係している可能性があります。
無限ループを回避する 無限ループが発生すると、ループ本体内で多数のインスタンスが繰り返し生成され、メモリ空間がすぐにいっぱいになる可能性があります。
頻繁に発生する Full GC プロセスの忘れられないトラブルシューティング http://caogen81.iteye.com/blog/1513345 オンライン FullGC の頻繁なトラブルシューティング https://blog.csdn.net/wilsonpeng3 /article/details/ 70064336/[JVM] オンライン アプリケーションのトラブルシューティング https://www.cnblogs.com/Dhouse/p/7839810.html JVM プロセスにおける FullGC のトラブルシューティング http://iamzhongyong。 iteye.com/blog/1830265JVM メモリ オーバーフローによる CPU 過剰のトラブルシューティング https://blog.csdn.net/nielinqi520/article/details/78455614Java メモリ リークトラブルシューティングのケース https://blog.csdn.net/aasgis6u/article/details/54928744 (5) 共通の JVM パラメーターのリファレンス:
説明 | インスタンス | |
---|---|---|
初期ヒープ サイズ、デフォルトの物理メモリの 1/64 | -Xms512M | |
最大ヒープ サイズ、デフォルトの物理メモリ 1 /4 | -Xms2G | |
新世代のメモリ サイズ、公式推奨は全体の 3/8 | #ヒープ #-Xmn512M | |
スレッド スタック サイズ、jdk1.5 以降のデフォルトは 1M、それ以前はデフォルトが 256k | -Xss512k | |
新世代と旧世代の比率を設定します。例:3の場合、若い世代と高齢世代の比率が1:3で、若者世代と高齢世代全体の1/4が若い世代であることを意味します。 #-XX:NewRatio=3 | -XX:SurvivorRatio=n | |
-XX:SurvivorRatio=8 | -XX:PermSize=n | |
-XX:PermSize=128M | #- XX:MaxPermSize=n | |
-verbose :class | クラス読み込み情報をコンソールに出力します | |
-verbose:gc | コンソールにガベージ コレクション ログを出力します。 | |
-XX: PrintGC | GC ログを出力します。内容は単純です。 | |
-XX: PrintGCDetails | 詳細な内容を含む GC ログを印刷 | |
-XX: PrintGCDateStamps GC ログの | タイムスタンプを追加します | |
-Xloggc:filename | GC ログ パスを指定します | |
-XX: UseSerialGC | 若い世代はシリアル コレクターを設定します Serial | |
-XX: UseParallelGC | 若い世代セットのパラレル コレクター Parallel Scavenge | |
-XX:ParallelGCThreads=n | Set Parallel Scavenge 収集中に使用される CPU の数。並列収集スレッドの数。 | |
-XX:MaxGCPauseMillis=n | Parallel Scavenge リサイクルの最大時間を設定します (ミリ秒) | |
-XX:GCTimeRatio=n | プログラム実行時間に対する Parallel Scavenge ガベージ コレクション時間を設定します。式は 1/(1 n) | |
-XX: UseParallelOldGC | 収集される古い世代を設定します。パラレル コレクターによる ParallelOld Collector | |
-XX: UseConcMarkSoupGC | 古い世代のコンカレント コレクター CMS | |
を設定します | -XX: CMSIncrementalMode | CMS コレクターを、単一 CPU の状況に適したインクリメンタル モードに設定します。 |
この記事は、php 中国語 Web サイトの | java チュートリアル
以上がjvm にパフォーマンス チューニングが必要なのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。