序文
Java プログラマーが JVM にインタビューすることは、質問することよりも難しいです。JVM の監視、オンライン OOM、CPU 負荷 100% などの質問がよく聞かれます。企業のオンライン問題に対処する番ではないかもしれませんが、面接であろうと、または、開発に対処するには、JVM オンラインの問題処理を理解する必要があります。
相対的に言えば、障害の問題を解決する場合でも、パフォーマンスのボトルネックに対処する場合でも、データ (ログ) を分析し、分析とトラブルシューティングを行い、問題を特定し、問題を解決するという一般的な考え方はほぼ同じです。実行データまたはログを取得できない場合、問題を特定する方法がありません。
幸いなことに、Java は、問題のトラブルシューティングに役立つ JVM 関連データの取得に役立つ JVM 監視ツールと関連手順を提出しました。
ツールのインストール
JVM があることだけはわかっていますが、その動作は目に見えないので、Windows のパフォーマンス モニターと同じように、JVM のリアルタイムの状態を監視するツールが必要です。JDK にも独自の視覚化ツールがあります。Java には 2 つの監視ツールがあります。 :
-
D:\opensource\jdk1.8\bin\jconsole.exe
-
D:\opensource\jdk1.8\bin\jvisualvm.exe
jコンソール
cmd コマンドラインから jconsole と入力すると、次のインターフェイスが表示されます
。java を選択して入力すると、入力後のメモリステータス、クラスロードステータス、スレッドステータスなどが表示されます。
Jvisualvm
cmd を実行し、jvisualvm と入力して、Java VisualVM を起動します。
左側のローカル メニューは Java プロセスです。プロセスを選択すると、右側にヒープ、クラスの読み込みステータス、および既製のステータスが表示されます。
jvisualvm は GC プラグインをインストールします
組み込みの jvisualvm は GC ガベージ コレクション機能を監視しないため、追加のプラグインをインストールする必要があります。
[ツール] -> [プラグイン] -> [利用可能なプラグイン] ページを選択します: ここでは Visual GC をインストールして、メモリの回復と各世代のステータスを確認できるようにします。チェックを入れた後、通常の [インストール] をクリックします。契約に同意するなど。ネットワークはあまり安定していないため、さらに数回試行する必要がある場合があります。設定でプラグイン センターのアドレスを変更できます。
次の手順に従ってアドレスを変更します。 プラグイン センターを見つけます。
http://visualvm.github.io/pluginscenters.html
対応する JDK バージョンを見つけます。
http://visualvm.github.io/archive/uc/8u40/updates.html
プラグインのアドレスをコピーします:
プラグインをインストールします。
次に、利用可能なプラグインで Visual GC を見つけます。
インストールが完了したら、現在の監視ページを閉じて再度開くと、プロファイラーの背後に追加の Visual GC ページがあることがわかります。
ここでは、JIT アクティビティ時間、クラスロードアクティビティ時間、GC アクティビティ時間と各世代の状況が表示されます。
現在のコースウェアで使用されている JDK バージョンは 1.8 であり、VisualVM にはまだ付属しています。1.9 以降のバージョンには付属していないため、追加でダウンロードする必要があります。ダウンロード用の github アドレスは次のとおりです。
https://visualvm.github.io/download.html
さらに、開発ツールが Intellij IDEA を使用している場合は、プラグイン VisualVM Launcher をダウンロードでき、左側のエントリで独自のプロジェクトを探すことなく、プラグインの起動を通じて上記のページに直接移動できます。
もちろん、他にもツールはありますが、これが当面のオールインワン障害処理ツールの主な開発となるため、このツールを使用して JVM の動作を分析し、後で最適化する予定です。 JVM の構成についてさらに理解することも必要です
JVM監視コマンド
運用環境では、さまざまな奇妙なパフォーマンスの問題が頻繁に発生しますが、Java が提供する JVM 監視コマンドを使用すると、監視と表示の効果を実現できます。関連するコマンドは次のとおりです。
名前 | 主効果 |
---|---|
jps | 実行中の Java プロセスを表示する |
jスタック | スレッドのスナップショットを印刷する |
jmap | ヒープメモリイメージファイルのエクスポート |
立つ | JVM 統計の表示 |
ジンフォ | JVM 設定パラメータをリアルタイムで表示および変更する |
ジャット | ヒープダンプ ファイルの分析に使用されます |
jps ビューの進行状況
jps は、実行中の Java プロセスを一覧表示し、仮想マシンのメイン クラス (Main Class、main() 関数が配置されているクラス) の名前とプロセス ID を表示し、パラメータは jps -help で表示できます。
オプション | 効果 |
---|---|
-q | プロセスIDのみを出力します |
-m | メインクラスのメイン関数に渡されるパラメータを出力します。 |
-l | メインクラスの完全なクラス名を出力します。プロセスが Jar パッケージを実行する場合は、jar パッケージの名前を出力します。 |
-v | プログラム起動時に指定するjvmパラメータ |
事例紹介:
jstack: スレッドのスナップショットを出力する
一般に、運用環境では、長時間の一時停止、スタック、デッドロック、長いリクエスト時間などの問題が発生した場合、スレッドのスナップショットを出力することで問題を分析して特定できます。デッドロック コードは次のとおりです。
public class DeadlockExample {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
synchronized (lock1) {
System.out.println("Thread 1 acquired lock 1");
try {
Thread.sleep(1000);
synchronized (lock2) {
System.out.println("Thread 1 acquired lock 2");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread() {
public void run() {
synchronized (lock2) {
System.out.println("Thread 2 acquired lock 2");
try {
Thread.sleep(1000);
synchronized (lock1) {
System.out.println("Thread 2 acquired lock 1");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
次に、最初に jps -l を実行してプロセスを表示し
、プロセス ID を取得し、jstack を使用して各スレッドのスナップショットを表示します。 jstack 27928
スレッド スナップショットから、現在のプロセス内のすべてのスレッドを確認できます。その中にはコードのスレッドも含まれており、状態はロックされています。同時に、「found 1 Deadlock」と表示されてデッドロックを検索し、デッドロックが発生した場所を示します。
jmap: ヒープスナップショットのエクスポート
実行するとjmap -histo pid
、現在のヒープ内の各クラスのインスタンス数とメモリ使用量が次のように出力されます。クラス名は各クラスのクラス名です ([B は byte 型、[C は char 型、[I は int 型]) 、バイト これは、このクラスのすべての例によって占有されるメモリ サイズであり、インスタンスはこのクラスのインスタンスの数です。
jmap -dump を実行して、指定したファイルにヒープ メモリのスナップショットをダンプします。
jmap -dump:format=b,file=/data/jvm/dumpfile_jmap.hprof PID を使用すると、現在のヒープ メモリのスナップショットを dumpfile_jmap.hprof ファイルにダンプし、メモリ スナップショットを分析できます。
通常、OOM 例外が発生した後に仮想マシンが自動的にダンプ ファイルを生成するように実稼働環境を構成します。
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users
たとえば、コードの無限ループがあり、それを一定時間実行するとメモリ オーバーフローが発生します。
public class Main {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
int i = 0;
while(true){
arrayList.add(new Main());
System.out.println(i++);
}
}
}
明らかな効果を得るために、ヒープを小さく設定してから、HeapDumpOnOutOfMemoryError をオンに設定します。
-Xms2m
-Xmx2m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=d:\
現在のアプリケーションに対して上記の VM パラメーターを設定すると、コードを実行すると次のような効果が得られます。
次に、hprof ヒープ スナップショット ファイルが見つかります。これは、jvisualvm ツールを通じてロードして分析できます。
スナップショット ファイルには、ファイル タイプが必要です。
ロード後、メモリ オーバーフローのエラー メッセージを直接確認できます。
スレッド名 main をクリックすると、メモリ オーバーフローの場所を直接見つけることができます。
jstat: 仮想マシン情報を監視します
jstat -gc pid 500 10
: pid はスレッドIDで、500ミリ秒ごとにJavaヒープの状態(各領域の容量、使用量、gc時間など)を出力 10回出力 jstatは各領域のメモリサイズの監視やクラスの監視も可能他の角度からの情報の読み込み
具体的には、jstat の詳しい使い方については、ググってください。以下は結果の比較です
S0C:第1サバイバエリアのサイズ
S1C:第2サバイバエリアのサイズ
S0U:第1サバイバエリアの使用サイズ
S1U:第2サバイバエリアの使用サイズ
EC:エデンエリアのサイズ
EU:使用量Eden エリアのサイズ
OC: オールド ジェネレーション サイズ
OU: オールド ジェネレーションの使用サイズ
MC: メソッド エリアのサイズ
MU: メソッド エリアの使用サイズ
CCSC: 圧縮クラス スペース サイズ CCSU
: 圧縮クラス スペースの使用サイズ
YGC: ヤング ジェネレーションのガベージ コレクション時間
YGCT: ヤング世代ガベージコレクション消費時間
FGC:旧世代のガベージコレクション時間
FGCT:旧世代のガベージコレクション時間
GCT:総ガベージコレクション時間
単位:KB
jinfo : プロセスパラメータの表示
jinfo(Configuration Info for Java) 仮想マシンの構成パラメータを確認し、仮想マシンの構成パラメータを調整するためにも使用できます。
多くの場合、Java アプリケーションでは、すべての Java 仮想マシン パラメータが指定されるわけではありません。現時点では、開発者は特定の Java 仮想マシン パラメータのデフォルト値を知らない可能性があります。この場合、ドキュメントを参照してパラメータのデフォルト値を取得することが必要になる場合があります。この検索プロセスは非常に困難になる場合があります。しかし、jinfo ツールを使用すると、開発者は Java 仮想マシンのパラメータの現在の値を簡単に見つけることができます。
jinfo は、実行時に特定の Java 仮想マシン パラメータの実際の値を表示できるだけでなく、実行時に一部のパラメータを変更して、それをすぐに有効にすることもできます。ただし、すべてのパラメーターが動的変更をサポートしているわけではありません。パラメータは、管理可能とマークされたフラグを使用してのみリアルタイムで変更できます。実際、この変更機能は非常に限られています。
VM フラグ:
デフォルト以外の VM フラグ: -XX:CICompilerCount=12 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=null -XX:InitialHeapSize=2097152 -XX:MaxHeapSize=209715200 -XX:MaxNewSize=69730304 -XX:MinHeapDeltaBytes=524288 -XX:OldSize=524288 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnownedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
コマンド ライン: -Xms2m -Xmx200m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d: \ -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\lib\idea_rt.jar=54817:D:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\bin -Dfile.encoding=UTF-8
jinfo -flags pid を通じて: 値が割り当てられたパラメータ値を表示します。
jinfo は、実行時に特定の Java 仮想マシン パラメータの実際の値を表示できるだけでなく、実行時に一部のパラメータを変更して、それをすぐに有効にすることもできます。ただし、すべてのパラメーターが動的変更をサポートしているわけではありません。パラメータは、管理可能とマークされたフラグを使用してのみリアルタイムで変更できます。
次のように、java -XX:+PrintFlagsInitial | grep manageable コマンドを使用して、管理可能な変更形式を表示できます。
- ブール型変更の場合: jinfo -flag ±parameter pid
- 非ブール型の場合: jinfo -flag パラメータ名=パラメータ値 pid
たとえば、GC ログのデモを次のように変更して印刷します。 jinfo -flag +PrintGCDetails PID
//查看进程
C:\Users\Administrator>jps -l
20924 org.example.Main
...
//查看是否设置PrintGCDetails参数配置
C:\Users\Administrator>jinfo -flag PrintGCDetails 20924
//增加jvm参数:打印GC详情
C:\Users\Administrator>jinfo -flag +PrintGCDetails 20924
//查看是否设置PrintGCDetails参数配置
C:\Users\Administrator>jinfo -flag PrintGCDetails 20924
-XX:+PrintGCDetails
...
オンラインCPU100%の問題を解決する
一般に、CPU 100% は基本的にコードの無限ループによって発生します。調査の中心的な考え方は、該当するサーバーを見つけて、どのプロセスのどのスレッドのどのコードが問題を引き起こしたかを特定し、その時点での異常コードの例を簡単に紹介することです。
最初のステップは、CPU を最も消費するプロセスを見つけることです。top -c を使用してプロセスを表示し、大きな P を入力して CPU 使用率に従って並べ替えます。
2 番目のステップは、プロセス内で最も多くの CPU を消費するスレッドを見つけることです。最も高い CPU を使用するプロセスを見つけ、プロセス ID (PID) を見つけ、コマンド top -Hp PID を使用してこのプロセスに対応するスレッドを見つけます。 CPU 使用率に従って並べ替えるには、大きな P を入力します。
最初の PID を取得するのは最も時間のかかるスレッド ID であり、次に printf "%x\n" PID を使用して PID を 10 進数から 16 進数に変換します (16 進数に変換する理由は、スタック内でスレッド ID が表現されるためです) 16進数で。)
[root@VM-4-2-centos ~]# printf "%x\n" 13759
35bf
次に、jstack を使用してプロセスのスタック情報を出力し、grep を使用して対応するスレッド関連のものを表示する必要があります。jstack プロセス ID | grep "スレッド ID" -C5 --color
jstack 30979 | grep "35bf" -C5 --color
この時点で、コードを印刷し、印刷されたスレッドのスナップショットから nid を照合することで、どのスレッドに時間がかかっているかを特定することができ、同時にコードを素早く特定することができ、どのクラスのどのメソッドかを確認することができます。原因の100%はCPUにあります。
遠隔モニタリング
コマンドに詳しくない場合、コマンドを使用して JVM を監視するのは面倒ですが、JVisualvm には jmx リモート機能が用意されています。デフォルトでは、ローカルホストの IP アドレスを介して RMI サービスを提供するため、リモート接続を有効にするために JVM パラメータをリモートで構成する必要があります。
-Xms256m
-Xmx512m
-Xss256m
-XX:PermSize=512m
-XX:MaxPermSize=1024m
-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=服务器IP
#远程服务的端口:
-Dcom.sun.management.jmxremote.port=9015
#客户端 rmi通信端口
-Dcom.sun.management.jmxremote.rmi.port=9015
#关闭ssl功能
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
次に、ローカルの Jvisualvm にリモート ホストを追加し
、jmx リモート接続を追加して、
リモート接続パラメータを設定し、SSL 接続をキャンセルします。
この記事は終了です。役に立った場合は、良いレビューをお願いします。あなたの励ましは次のとおりです。私の最大のモチベーション