JVM パフォーマンス チューニングおよび監視ツールの使用に関する詳細な説明

JVM パフォーマンス チューニングおよび監視ツールの使用に関する詳細な説明

[[280944]]

実際のエンタープライズ レベルの Java アプリケーションの開発と保守では、次のような問題に遭遇することがあります。

  • OutOfMemoryError、メモリ不足
  • メモリリーク
  • スレッドデッドロック
  • ロック競合
  • JavaプロセスがCPUを大量に消費する
  • ......

これらの問題は、日常の開発や保守において多くの人が無視するかもしれません (たとえば、上記の問題に遭遇したときに、問題の根本原因を掘り下げずに、単にサーバーを再起動したり、メモリを増やしたりする人もいます)。しかし、これらの問題を理解して解決できることは、Java プログラマーが進歩するために必要な要件です。この記事では、議論の出発点となることを目指して、一般的に使用されている JVM パフォーマンス チューニングおよび監視ツールをいくつか紹介します。

さらに、運用・保守、開発、テストのいずれの場合でも、これらの監視およびチューニング ツールの使用方法を習得する必要があります。

A. jps (Java 仮想マシン プロセス ステータス ツール)

jps は主に、JVM で実行されているプロセスのステータス情報を出力するために使用されます。構文の形式は次のとおりです。

  1. jps [オプション] [ホストID]

hostid が指定されていない場合は、現在のホストまたはサーバーがデフォルトになります。

コマンドラインパラメータオプションは次のように記述されます。

  1. -q はクラス名、Jar 名、およびメインメソッドに渡されるパラメータを出力しません。
  2. -mはメインメソッドに渡されたパラメータを出力します
  3. -l メインクラスまたはJarの完全修飾名を出力します
  4. -vはJVMに渡されたパラメータを出力します

例えば:

  1. ルート@ubuntu:/# jps -m -l
  2. 2458 org.artifactory.standalone.main.Main /usr/ローカル/artifactory-2.2.5/etc/jetty.xml
  3. 29920 com.sun.tools.hat.Main -port 9998 /tmp/dump.dat
  4. 3149 org.apache.catalina.startup.Bootstrap の開始
  5. 30972 sun.tools.jps.Jps -m -l
  6. 8247 org.apache.catalina.startup.Bootstrap の開始
  7. 25687 com.sun.tools.hat.Main -port 9999 dump.dat
  8. 21711 mrf-center.jar

B. スタック

jstack は主に、Java プロセス内のスレッド スタック情報を表示するために使用されます。構文の形式は次のとおりです。

  1. jstack [オプション] pid
  2. jstack [オプション] 実行可能コア
  3. jstack [オプション] [server-id@]remote-hostname-または-ip

コマンドラインパラメータオプションは次のように記述されます。

  1. -l 長いリストでは、追加のロック情報が出力されます。デッドロックが発生した場合、jstack -l pid を使用してロックの保持状態を観察できます。 -m 混合モードでは、Java スタック情報だけでなく、C/C++ スタック情報 (ネイティブ メソッドなど) も出力されます。

jstack はスレッド スタックを見つけることができます。スタック情報に基づいて特定のコードを見つけることができるため、JVM パフォーマンス チューニングで広く使用されています。 Java プロセスで最も CPU を消費する Java スレッドを見つけて、スタック情報を探す例を見てみましょう。使用されるコマンドは、ps、top、printf、jstack、grep です。

最初のステップは、Java プロセス ID を見つけることです。サーバーにデプロイした Java アプリケーションの名前は mrf-center です。

  1. root@ubuntu:/# ps -ef | grep mrf-center | grep -v grep
  2. ルート 21711 1 1 14:47 pts/3 00:02:10 java -jar mrf-center.jar

プロセス ID は 21711 です。2 番目のステップは、プロセス内で CPU を最も消費するスレッドを見つけることです。 ps -Lfp pid または ps -mp pid -o THREAD, tid, time または top -Hp pid を使用できます。ここでは3番目を使います。出力は次のようになります。

TIME 列は、各 Java スレッドによって消費された CPU 時間です。 CPU 時間が最も長いスレッドはスレッド ID 21742 です。

  1. printf "%x\n" 21742

21742 の 16 進数値は 54ee であり、以下で使用されます。

さて、次のステップでは、ついに jstack の出番です。jstack は、プロセス 21711 のスタック情報を出力するために使用され、次のようにスレッド ID の 16 進値に従って grep されます。

  1. ルート@ubuntu:/# jstack 21711 | grep 54ee
  2. "PollIntervalRetrySchedulerThread" prio=10 tid=0x00007f950043e000 nid=0x54ee in Object.wait [0x00007f94c6eda000]

PollIntervalRetrySchedulerThread クラスの Object.wait によって CPU が消費されていることがわかります。自分のコードを探したところ、次のコードを見つけました。

  1. // アイドル待機
  2. getLog.info( "スレッド[" + getName() + "]はアイドル状態で待機しています..." );
  3. スケジューラスレッド状態 = PollTask​​SchedulerThreadState.IdleWaiting;
  4. long now = System.currentTimeMillis;
  5. 長い待機時間 = 現在 + getIdleWaitTime;
  6. long timeUntilContinue = waitTime - 現在;
  7. synchronized(sigLock) {try {
  8. if(!停止.get) {
  9. sigLock.wait(継続するまで待機)。
  10. }
  11. } キャッチ (InterruptedException を無視) {
  12. }
  13. }

ポーリングタスクのアイドル待機コードです。上記の sigLock.wait(timeUntilContinue) は、前の Object.wait に対応します。

C. jmap (メモリマップ) と jhat (Java ヒープ分析ツール)

jmap はヒープメモリの使用量を表示するために使用され、通常は jhat と組み合わせて使用​​されます。

jmap の構文形式は次のとおりです。

  1. jmap [オプション] pid
  2. jmap [オプション] 実行可能コア
  3. jmap [オプション] [server-id@]remote-hostname-または-ip

64 ビット JVM 上で実行している場合は、-J-d64 コマンド オプション パラメータを指定する必要がある場合があります。

  1. jmap -permstat pid

プロセスのクラス ローダーと、クラス ローダーによってロードされた永続生成オブジェクトの情報を印刷し、次の図に示すように、クラス ローダー名、オブジェクトが生きているかどうか (信頼できない)、オブジェクトのアドレス、親クラス ローダー、ロードされたクラスのサイズなどを出力します。

jmap -heap pid を使用して、使用されている GC アルゴリズム、ヒープ構成パラメータ、各世代のヒープ メモリ使用量など、プロセスのヒープ メモリ使用量を表示します。たとえば、次の例をご覧ください。

  1. root@ubuntu:/# jmap -heap 21711
  2. プロセス ID 21711接続しています。お待ちください...
  3. デバッガーが正常に接続されました。
  4. サーバーコンパイラが検出されました。
  5. JVMバージョンは20.10-b01です
  6.  
  7. スレッドローカルなオブジェクト割り当てを使用します。
  8. 4スレッドによる並列GC
  9.  
  10. ヒープ構成:
  11. 最小ヒープ空き率 = 40
  12. 最大ヒープ空き率 = 70
  13. 最大ヒープサイズ = 2067791872 (1972.0MB)
  14. 新しいサイズ = 1310720 (1.25MB)
  15. 最大新規サイズ = 17592186044415 MB
  16. 古いサイズ = 5439488 (5.1875MB)
  17. 新しい比率 = 2
  18. 生存率 = 8
  19. パーマサイズ = 21757952 (20.75MB)
  20. 最大パーミッションサイズ = 85983232 (82.0MB)
  21.  
  22. ヒープ使用量:
  23. PS 若い世代
  24. エデンスペース:
  25. 容量 = 6422528 (6.125MB)
  26. 使用済み = 5445552 (5.1932830810546875MB)
  27. 空き= 976976 (0.9317169189453125MB)
  28. 84.78829520089286% 使用済み
  29. から 空間
  30. 容量 = 131072 (0.125MB)
  31. 使用済み = 98304 (0.09375MB)
  32. 空き= 32768 (0.03125MB)
  33. 75.0%使用
  34.  空間
  35. 容量 = 131072 (0.125MB)
  36. 使用済み = 0 (0.0MB)
  37. 無料= 131072 (0.125MB)
  38. 0.0% 使用済み
  39. PS 旧世代
  40. 容量 = 35258368 (33.625MB)
  41. 使用済み = 4119544 (3.9287033081054688MB)
  42. 空き= 31138824 (29.69629669189453MB)
  43. 11.683876009235595% 使用済み
  44. PSパーマ生成
  45. 容量 = 52428800 (50.0MB)
  46. 使用済み = 26075168 (24.867218017578125MB)
  47. 空き= 26353632 (25.132781982421875MB)
  48. 49.73443603515625% 使用済み
  49. ....

jmap -histo[:live] pid を使用して、ヒープ メモリ内のオブジェクトの数とサイズの統計ヒストグラムを表示します。 live が含まれる場合、次のようにライブ オブジェクトのみがカウントされます。

  1. root@ubuntu:/# jmap -histo:live 21711 |もっと
  2. num #インスタンス #バイト クラス-------------------------------------------------  
  3. 1: 38445 5597736 <constMethodKlass>
  4. 2: 38445 5237288 <メソッドクラス>
  5. 3: 3500 3749504 <定数プールクラス>
  6. 4: 60858 3242600 <symbolKlass>
  7. 5: 3500 2715264 <インスタンスクラスクラス>
  8. 6: 2796 2131424 <定数プールキャッシュクラス>
  9. 7: 5543 1317400 [私
  10. 8: 13714 1010768 [C
  11. 9: 4752 1003344 [B
  12. 10: 1225 639656 <メソッドデータクラス>
  13. 11: 14194 454208 java.lang.String
  14. 12: 3809 396136 java.lang.クラス
  15. 13: 4979 311952 [S
  16. 14: 5598 287064 [[私
  17. 15: 3028 266464 java.lang.reflect.メソッド
  18. 16: 280 163520 <objArrayKlassKlass>
  19. 17: 4355 139360 java.util.HashMap$エントリ
  20. 18: 1869 138568 [Ljava.util.HashMap$Entry;
  21. 19: 2443 97720 java.util.LinkedHashMap$エントリ
  22. 20: 2072 82880 java.lang.ref.SoftReference
  23. 21: 1807 71528 [Ljava.lang.Object;
  24. 22: 2206 70592 java.lang.ref.WeakReference
  25. 23: 934 52304 java.util.LinkedHashMap
  26. 24: 871 48776 java.beans.メソッド記述子
  27. 25: 1442 46144 java.util.concurrent.ConcurrentHashMap$HashEntry
  28. 26: 804 38592 java.util.HashMap
  29. 27: 948 37920 java.util.concurrent.ConcurrentHashMap$セグメント
  30. 28: 1621 35696 [Ljava.lang.Class;
  31. 29: 1313 34880 [Ljava.lang.String;
  32. 30: 1396 33504 java.util.LinkedList$エントリ
  33. 31: 462 33264 java.lang.reflect.フィールド
  34. 32: 1024 32768 java.util.Hashtable$Entry
  35. 33: 948 31440 [Ljava.util.concurrent.ConcurrentHashMap$HashEntry;

クラス名はオブジェクト タイプで、次のようになります。

  1. Bバイト
  2. C文字 
  3. Dダブル 
  4. Fフロート 
  5.  
  6. J ロング
  7. Z ブール値
  8. [ 配列、例えば [I はint []を意味します
  9. [L+クラス名その他のオブジェクト

もう 1 つの一般的な状況は、jmap を使用してプロセスのメモリ使用量をファイルにダンプし、jhat を使用してそれを分析して表示することです。 jmap dump コマンドの形式は次のとおりです。

  1. jmap -dump:format=b,file=dumpFileName pid

上記のプロセス ID 21711 もダンプしました。

  1. root@ubuntu:/# jmap -dump:format=b,file=/tmp/dump.dat 21711
  2. ヒープを/tmp/dump.datダンプしています...
  3. ヒープダンプファイルが作成されました

ダンプされたファイルは、MAT や VisualVM などのツールを使用して表示できます。ここでは jhat を使用して表示します。

  1. root@ubuntu:/# jhat -port 9998 /tmp/dump.dat
  2. /tmp/dump.datから読み込んでいます...
  3. ダンプ ファイルは Tue Jan 28 17:46:14 CST 2014 に作成されました。スナップショットを読み取り、解決しています...
  4. 132207 個のオブジェクトを解決しています...
  5. 参照を追跡すると、26 個のドットが予想されます........................
  6. 重複参照の削除.............................
  7. スナップショットが解決されました。
  8. ポート 9998HTTP サーバーを起動しました。サーバーの準備整いました。

ダンプ ファイルが大きすぎる場合は、最大ヒープ メモリを指定するために -J-Xmx512m などのパラメータ (つまり、jhat -J-Xmx512m -port 9998 /tmp/dump.dat) を追加する必要がある場合があります。次に、ブラウザにホストアドレス 9998 を入力して表示します。

上記の赤枠で囲んだ部分を自分で探索することができます。最後の項目は OQL (Object Query Language) をサポートします。

D. jstat (JVM 統計監視ツール)

構文の形式は次のとおりです。

  1. jstat [ 一般オプション |出力オプション vmid [間隔[秒|ミリ秒] [カウント]] ]

vmid は Java 仮想マシン ID であり、通常は Linux/Unix システム上のプロセス ID です。間隔はサンプリング時間間隔です。 count はサンプルの数です。たとえば、次の出力は、サンプリング間隔が 250 ミリ秒でサンプリング数が 4 の GC 情報を示しています。

  1. ルート@ubuntu:/# jstat -gc 21711 250 4
  2. S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
  3. 192.0 192.0 64.0 0.0 6144.0 1854.9 32000.0 4111.6 55296.0 25472.7 702 0.431 3 0.218 0.649
  4. 192.0 192.0 64.0 0.0 6144.0 1972.2 32000.0 4111.6 55296.0 25472.7 702 0.431 3 0.218 0.649
  5. 192.0 192.0 64.0 0.0 6144.0 1972.2 32000.0 4111.6 55296.0 25472.7 702 0.431 3 0.218 0.649
  6. 192.0 192.0 64.0 0.0 6144.0 2109.7 32000.0 4111.6 55296.0 25472.7 702 0.431 3 0.218 0.649

上記の列の意味を理解するには、まず JVM ヒープ メモリ レイアウトを確認します。

次のことがわかります。

  1. ヒープメモリ = 若い世代 + 古い世代 + 永久世代
  2. 若い世代 = エデンエリア + 2 つのサバイバーエリア ( FromTo )

それでは、各列の意味を説明しましょう。

  1. S0C、S1C、S0U、S1U: サバイバー 0/1 ゾーンの容量と使用法
  2. EC、EU: エデン地域の容量と使用状況
  3. OC、OU: 旧世代の容量と使用量
  4. PC、PU: 永久発電容量と使用量
  5. YGC、YGT: 若い世代のGC時間とGC時間
  6. FGC、FGCT:フルGC 回数とフルGC 期間
  7. GCT: 合計GC時間

E. hprof (ヒープ/CPU プロファイリング ツール)

Hprof は CPU 使用率を表示し、ヒープメモリの使用量をカウントできます。

構文の形式は次のとおりです。

  1. java -agentlib:hprof[=オプション] ToBeProfiledClass
  2. java -Xrunprof[:options] プロファイル対象クラス
  3. javac -J-agentlib:hprof[=オプション] ToBeProfiledClass

完全なコマンド オプションは次のとおりです。

  1. オプション 名前 および値の説明デフォルト 
  2. --------------------- ----------- -------  
  3. ヒープ=ダンプ|サイト|すべてヒーププロファイリングすべて 
  4. cpu=サンプル|回|古いCPU使用率オフ 
  5. monitor=y|n モニター競合 n
  6. format=a|b text(txt)または バイナリ 出力
  7. file=<file> java.hprof[.txt]ファイルデータを書き込みます
  8. net=<host>:<port> ソケット経由でデータを送信 
  9. depth=<サイズ> スタックトレースの深さ 4
  10. interval=<ms> サンプル間隔ミリ秒) 10
  11. cutoff=<値>出力カットオフポイント 0.0001
  12. lineno=y|n トレース行番号?ええ
  13. thread=y|nトレース内のスレッドですか?
  14. doe=y|n 終了時にダンプしますか?ええ
  15. msa=y|n Solaris マイクロステートアカウンティング n
  16. =y|n 出力  <ファイル> y
  17. verbose=y|n ダンプに関するメッセージを出力します y

公式ガイドからの例をいくつか紹介します。

CPU 使用率サンプリング プロファイリングの例 (cpu=samples):

  1. java -agentlib:hprof=cpu=samples,interval=20,depth=3 こんにちは

上記は、スタック深度 3 で 20 ミリ秒ごとにサンプリングされた CPU 消費情報です。生成されたプロファイル ファイル名は java.hprof.txt で、現在のディレクトリにあります。

CPU 使用時間プロファイリングの例 (cpu=times)。 CPU 使用率サンプリング プロファイルと比較すると、各メソッド呼び出しの開始と終了に至るまで、より詳細な CPU 消費情報を取得できます。その実装ではバイトコード インジェクション テクノロジー (BCI) を使用します。

  1. javac -J-agentlib:hprof=cpu=times Hello.java

ヒープ割り当てプロファイリングの例 (heap=sites):

  1. javac -J-agentlib:hprof=heap=sites Hello.java

上記のヒープ割り当てプロファイリングよりも詳細なヒープ ダンプ情報を生成できるヒープ ダンプ (heap=dump) の例:

  1. javac -J-agentlib:hprof=heap=dump Hello.java

<<:  Citrix: 協力エコシステムを拡大し、デジタルの未来をインテリジェントに強化

>>:  オープンソースのKubernetes監視ツールトップ10

推薦する

高級品ネットワークの将来は心配だ:ドメイン名 Vip.com は傍観者のままなのか?

近年、高級品市場は低迷している。 VIPchina.comの破産疑惑により、ファッショントレンドをリ...

OpenStack仮想マシンのメタデータを取得する方法

1. OpenStackメタデータサービスについてOpenStack 仮想マシンは、ネットワーク カ...

本当のWeiboマーケティングの復活(I)

私はずっとWeiboマーケティングについて書きたいと思っていました。2012年11月、18人のインタ...

underhost: オランダのサーバー、防弾ホスト、著作権侵害の申し立てを無視、コンテンツ制限なし

Underhost のオランダのデータセンターには、2 つの特別なサーバー、「防弾サーバー」がありま...

SAP製品ライフサイクル管理ソリューションは進化と拡大を続けています

2020 年 10 月は、SAP 製品ライフサイクル管理にとって重要な節目となります。 SAP はシ...

中国モバイルインターネット2019春レポート!

インターネット経済全体の成長は、伝統的な経済よりもはるかに速いペースで進んでいます。同時に、オンライ...

信頼できるSEO会社を選ぶ方法

SEO会社の選び方現在、SEO サービスを必要としている企業は数多くあります。一般的に、SEO を使...

タレントステーションの経営戦略に関する考察

最近、A5のウェブサイトでタレントステーションに焦点を当てた記事をたくさん見ていて、とても嬉しいです...

2021年上半期のクラウドコンピューティングの振り返り:利益、セキュリティ、政府対応が3つの新たな課題に

8月27日、天津市国有資産監督管理委員会は「国有企業のクラウド移行の加速と国有資産クラウドシステムの...

Ceph分散ストレージシステムの最適化分析

[[409074]]前回の記事「Ceph 分散ストレージ システム アーキテクチャ研究のレビュー」で...

優れたウェブサイト編集者がウェブサイトの最適化を行うには、どのような資質が必要ですか?

ご存知のとおり、ウェブサイトの最適化プロセスは実際には段階的なプロセスです。このプロセスでは、サイト...

AIがクラウドネットワークにもたらす破壊力

AI がクラウド プラットフォームの成長を加速し、新世代の AI 駆動型ツールがクラウド環境を管理で...

インターネット マーケティングはあなたを救うために何ができるでしょうか?

インターネットが急速に発展している時代、すべての企業や個人がこのプラットフォームを通じて利益を上げる...

SEM医療ネットワークプロモーション

筆者は2月2日に「医療SEMにおけるWeb編集者の手ほどき」を公開したが、友人から実践は難しいと言わ...

インターネット起業は続くが、なぜセルフメディアのウェブサイト運営者は次々と失敗するのか?

インターネット起業ブームにより、夢を持った多くの起業家が誕生しており、セルフメディアのウェブマスター...