一般的な jvm チューニング操作の詳細な記録

最近、多くの友人から、JVM チューニングの知識はかなり身に付いたものの、実際の作業ではいつ JVM をチューニングすればよいかがわからないという話がありました。今日は、JVM チューニングのいくつかのシナリオを紹介します。

CPU 使用率が高すぎる
. CPU 使用率が高すぎる. 状況に応じて議論する必要がある. ビジネスでのアクティビティがあり、大量のトラフィックが突然発生し、CPU 使用率が高くなるためでしょうか.イベントが終了するとドロップします. この場合, あまり心配する必要はありません. リクエストが多いほど, より多くのスレッドを処理する必要があるため, これは正常です. そうは言っても、サーバーの構成自体が貧弱で、CPU が 1 つのコアしかない場合、この場合、もう少しトラフィックが増えると CPU リソースが使い果たされる可能性があります. この時点で、最初に構成のアップグレードを検討する必要があります.

2 番目のケースでは、CPU 使用率が長時間にわたって高すぎます. この場合、プログラムには多くのループまたは無限ループを含むコードが含まれている可能性があります. トラブルシューティングの手順は次のとおりです。
(1) top コマンドを使用して CPU 使用率を確認し
ここに画像の説明を挿入
、CPU が高すぎるプロセスを特定します。Linux の場合、top コマンドで取得したプロセス番号は、jps ツールで取得した vmid と同じです:
ここに画像の説明を挿入
(2) top -Hp コマンドを使用してスレッドの状態を確認すると、スレッド ID が7287
ここに画像の説明を挿入
のスレッドが存在することがわかります。CPUを占有している

(3) スレッド番号を 16 進数に変換し、
ここに画像の説明を挿入
16 進数を書き留めます。以下で使用します

(4) jstack ツールを使用してスレッド スタックを表示する

ここに画像の説明を挿入
jstack ツールを使用して現在のスレッド スタックを出力し、grep コマンドを前の手順で取得したスレッドの 16 進数の ID と組み合わせて使用​​し、このスレッドの実行ステータスを特定します。jstack の後ろの 7268 は、手順で見つけたプロセス番号です。 (1). grep の後には、手順 (2) と (3) で見つけたスレッド番号があります。

出力結果を見ると、スレッドが動いているのがわかります.com.spareyaya.jvm.service.EndlessLoopService.serviceメソッドを実行すると、コード行番号が19行目なので、コードの19行目に行くと、ブロックしてループ内にあるかどうかを確認し、問題を突き止めます。

(2 番目のケース - デッドロック)

デッドロックは、最初のシナリオほど明白ではありません. Web アプリケーションは、複数の要求を処理するマルチスレッド プログラムである必要があります. プログラムがデッドロックした後、デッドロックされたスレッドは待機状態 (WAITING または TIMED_WAITING) になり、待機状態になります。スレッドはCPUを占有せず、消費されるメモリは非常に限られており、パフォーマンスはリクエストを処理できず、最終的にタイムアウトになる可能性があります。デッドロック状況があまりない場合、この状況を検出するのは容易ではありません。

jstack ツールを使用して
(1) jps を表示し、Java プロセスを表示できます。
ここに画像の説明を挿入

(2) jstack ビューのデッドロックの問題

多くの場合、Web アプリケーションには多くのワーカー スレッドがあり、特に同時実行性が高い場合はスレッド数が多くなり、このコマンドの出力は非常に大きくなります。jstack の最大の利点は、デッドロック情報 (どのスレッドがそれを生成したかを含む) を最後まで出力するため、最後の内容を確認するだけでよいことです。

ここに画像の説明を挿入
デッドロックが発見され、その理由は一目瞭然です。

( 3 番目のケース - メモリ リーク)

Java と C++ の最大の違いは、前者は未使用のメモリを自動的に再利用するのに対し、後者はプログラマが手動で解放する必要があることです。C++ では、メモリの解放を忘れるとメモリ リークが発生します。ただし、jvm がメモリを再利用した後にメモリ リークが発生しないとは考えないでください。

プログラムでメモリ リークが発生すると、プロセスの使用可能なメモリが徐々に減少し、最終的に OOM エラーが発生します。OOM エラーが発生した後、メモリが十分に大きくないと考えられる場合があるため、-Xmx パラメータを増やしてから、アプリケーションを再起動します。この結果、一定期間後も OOM が表示されます。最終的に、最大ヒープ メモリを増やすことはできなくなり、その結果、アプリケーションを時々再起動するしかなくなります。

メモリ リークで考えられるもう 1 つの症状は、要求に対する応答時間の増加です。これは、頻繁な GC が他のすべてのスレッドを中断するためです (Stop The World)。

このシナリオをシミュレートするために、次のプログラムが使用されました

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        while (true) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            main.run();
        }
    }

    private void run() {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                // do something...
            });
        }
    }
}

実行パラメータは -Xms20m -Xmx20m -XX:+PrintGC で、使用可能なメモリを少し減らし、gc が発生したときに情報を出力します。実行結果は次のとおりです。

...
[GC (Allocation Failure)  12776K->10840K(18432K), 0.0309510 secs]
[GC (Allocation Failure)  13400K->11520K(18432K), 0.0333385 secs]
[GC (Allocation Failure)  14080K->12168K(18432K), 0.0332409 secs]
[GC (Allocation Failure)  14728K->12832K(18432K), 0.0370435 secs]
[Full GC (Ergonomics)  12832K->12363K(18432K), 0.1942141 secs]
[Full GC (Ergonomics)  14923K->12951K(18432K), 0.1607221 secs]
[Full GC (Ergonomics)  15511K->13542K(18432K), 0.1956311 secs]
...
[Full GC (Ergonomics)  16382K->16381K(18432K), 0.1734902 secs]
[Full GC (Ergonomics)  16383K->16383K(18432K), 0.1922607 secs]
[Full GC (Ergonomics)  16383K->16383K(18432K), 0.1824278 secs]
[Full GC (Allocation Failure)  16383K->16383K(18432K), 0.1710382 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1829138 secs]
[Full GC (Ergonomics) Exception in thread "main"  16383K->16382K(18432K), 0.1406222 secs]
[Full GC (Allocation Failure)  16382K->16382K(18432K), 0.1392928 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1546243 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1755271 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1699080 secs]
[Full GC (Allocation Failure)  16382K->16382K(18432K), 0.1697982 secs]
[Full GC (Ergonomics)  16383K->16382K(18432K), 0.1851136 secs]
[Full GC (Allocation Failure)  16382K->16382K(18432K), 0.1655088 secs]
java.lang.OutOfMemoryError: Java heap space

gc がますます多くのメモリを占有しているにもかかわらず、プログラム内の一部のオブジェクトを再利用できないことが示されていることがわかります。ただし、上記のプログラム オブジェクトはすべてメソッド内で定義され、ローカル変数に属しているため、メソッドが結果を実行した後、参照されたオブジェクトは gc 中に再利用される必要がありますが、ここでは明らかにそうではありません。

リサイクルできないオブジェクトを見つけるために、実行パラメーター -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.bin を追加します。これは、OOM が発生したときにヒープ メモリ情報をダンプすることを意味します。異常が出るまでプログラムを実行し、heap.dump ファイルを取得し、eclipse の MAT プラグインを使用して解析します (インストールされていない場合は、最初にインストールする必要があります)。

次に File->Open Heap Dump... 、ダンプしたばかりのファイルを選択し、Leak Suspects を選択します。

ここに画像の説明を挿入
jmap ツールを使用してメモリ スナップショットをダンプする

jmap は、指定された Java プロセスのメモリ スナップショットをダンプできます。効果は最初の処理方法と同じです。違いは、OOM を待たずにダンプできることです。ダンプされたスナップショットははるかに小さくなります。

jmap -dump:live, format=b, file=heap.bin 24836
この時点で、heap.bin のメモリ スナップショット ファイルが取得され、Eclipse を使用して分析できます。

今日はここまでです、また後で。.

おすすめ

転載: blog.csdn.net/leaning_java/article/details/130410693