Arthas 診断ツールを 5 分間で 8 つの質問から学ぶ方法を教えます

序文

Arthas これは、開発者の間で非常に人気のあるアリババのオープン ソース Java 診断ツールです。

次のような同様の問題に遭遇し、途方に暮れている場合は、Arthasそれらを解決するのに役立ちます。

  1. このクラスはどの jar パッケージからロードされますか? さまざまな種類の関連する例外が報告されるのはなぜですか?

  2. 変更したコードが実行されないのはなぜですか? 私がコミットしていないということでしょうか?分岐がおかしい?

  3. 問題が発生し、オンラインでデバッグできない場合、ログを追加することによってのみ再発行できますか?

  4. オンラインで特定のユーザーのデータ処理に問題が発生しましたが、オンラインでもデバッグできず、オフラインでも再現できません。

  5. システムの健全性をグローバルに把握できますか?

  6. JVM のリアルタイムの実行状況を監視する方法はありますか?

  7. アプリケーションのホットスポットをすばやく見つけてフレーム グラフを生成する方法は?

  8. JVM内から直接クラスのインスタンスを見つける方法は?

Arthas の公式ドキュメント ( ) には、これら 8 つの質問https://arthas.aliyun.com/docに対する回答や標準的な解決策はありません。

浮気

これは殺すか埋めないか!! !

殺すか埋めるか

文章

「以下は、長年 Arthas を使用してきた経験に基づいた、これら 8 つの問題に対する著者の詳細な解決策です。質問がある場合は、コメント欄で遠慮なく指摘してください。」

準備

最初に私のテストコードを与えます

package com.shockang.study;

import com.alibaba.fastjson.JSON;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;

import java.util.List;
import java.util.concurrent.TimeUnit;

public class ArthasDemo {
    public static void main(String[] args) {
        String s = "[{\"name\":\"zhangsan\",\"age\":\"10\",\"telephone\":\"123456\",\"interests\":[\"sing\",\"dance\",\"rap\"]},\n" +
                "{\"name\":\"lisi\",\"age\":\"20\",\"telephone\":\"123457\",\"interests\":[\"sing\",\"swim\"]},\n" +
                "{\"name\":\"wangwu\",\"age\":\"30\",\"telephone\":\"123458\",\"interests\":[\"sing\",\"program\"]}]";
        //模拟一遍遍的调用方法的过程
        for (; ; ) {
            System.out.println(new ArthasDemo().convert(s));
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private List<People> convert(String s) {
        return JSON.parseArray(s, People.class);
    }


    @Getter
    @Setter
    @ToString
    @FieldDefaults(level = AccessLevel.PRIVATE)
    private static class People {
        /**
         * 姓名
         */
        String name;
        /**
         * 年龄
         */
        String age;
        /**
         * 电话
         */
        String telephone;
        /**
         * 兴趣列表
         */
        List<String> interests;
    }
}

以下は、コンソールでの正常な印刷の結果です

/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/bin/java ...
[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]
[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]

Arthas をダウンロードして実行する

以下の図の手順に従って、アタッチする Java プロセスを選択します。

Arthas をダウンロードして実行する

Web コンソールへのアクセス

アタッチが成功したら、Google Chrome 入力を開いてhttp://127.0.0.1:3658/ WebConsole を開くことができます。

(Tucao Mac OS Safari ブラウザはサポートしていません)

WebConsoleの一番便利なところは、複数のタブを開いて同時に操作できること

質問 1: このクラスはどの jar パッケージからロードされますか? さまざまな種類の関連する例外が報告されるのはなぜですか?

さまざまな「依存関係の競合」を処理するときに、この問題に遭遇することがよくあります.一部のクラスのフルネームはまったく同じであり、クラスがどのjarパッケージからロードされているかを従来の方法で解決することはできません.

心配しないで、以下の私の解決策を見てください。

  1. sc

コマンドを使用して sc 、キーワードを含むクラスが現在の JVM にロードされているかどうかを漠然と確認し、その完全な名前を取得します。

sc -d このコマンドを使用して、後で使用する classLoaderHash を取得することに注意してください 。

sc -d *ArthasDemo*

sc -d コマンド

  1. クラスローダ

classloader クラスファイルがどのjarパッケージに由来するかを確認することにより 

コマンドを使用して コマンド ラインをクリアできますcls が、この単純なコマンドの公式ドキュメントは見つかりません。. .

次の値は、上記の最初の手順で取得したハッシュ値で埋められることに注意してください classloader -c . クラス ファイル パスは「/」で区切られ、.class で終わる必要があります。

[arthas@3633]$ classloader -c 18b4aac2 -r com/shockang/study/ArthasDemo.class
file:/Users/shockang/code/concurrentbook/target/classes/com/shockang/study/ArthasDemo.class
Affect(row-cnt:1) cost in 0 ms.

上記はクラス ファイルのパスを示しています. クラス ファイルが jar パッケージからのものである場合は、公式ドキュメントに示されている例のように、jar パッケージのパスを表示できます。

$ classloader -c 1b6d3586 -r java/lang/String.class
jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/String.class

質問 2: 変更したコードが実行されないのはなぜですか? 私がコミットしていないということでしょうか?分岐がおかしい?

 非常に使いやすく、コマンドを使用することwatch を お勧めします 。tt

これらの 2 つのコマンドは、メソッド呼び出しプロセスを表示するために使用されます. 違いは、 watch このコマンドはメソッドの呼び出し状況を 1 回出力することであり、 tt このコマンドは最初に増加する呼び出しリストを生成し、次にそれらの 1 つを監視用に指定できます.

  1. watch コマンドを使用して 、メソッド呼び出しを表示します。ArthasDemo クラスの convert メソッド呼び出しを確認します。

監視コマンド

watch com.shockang.study.ArthasDemo convert "{params,target,returnObj}" -f -x 4

watch 完全なクラス名とメソッド名、および OGNL 式が続きます。-f は、正常に戻るか異常に戻るかを観察することを意味し、-x は、出力結果の属性トラバーサルの深さを意味し、デフォルトは 1 です。

何も考えずに 4 と書くことをお勧めします. これは筆者の経験上最大のトラバーサル深度です. どんなに大きくても対応しません.

  1. tt コマンドを使用して メソッド呼び出しを観察します.tt コマンドは「複数の呼び出し」を表示し、そのうちの 1 つを選択して観察できますが、出力結果が複数レベルのネストの場合、それを読み取る方法はありませんが、結果を表示することは できwatch ます「マルチレベルのネスティング」の。

tt -t を使用して、現在のメソッドの各呼び出しの環境シーンを記録します

tt -t コマンド

tt -t com.shockang.study.ArthasDemo convert

TIMESTAMP はメソッド呼び出しが発生した時刻、COST は呼び出し時間 (ms)、IS-RET は正常に戻ったかどうか、IS-EXP は異常に戻ったかどうか、OBJECT はオブジェクトのハッシュ値を示します。

特定のタイム スライスの情報については、-i パラメータの後に対応する INDEX 番号を指定して、その詳細情報を表示できます。

tt -i コマンド

興味のあるリストが図に出力できるのは、その toString メソッドが呼び出されているためです. java.lang.Object クラスの toString メソッドを書き直さないと、ハッシュ値だけが表示されます.

  1. コードが提出されたかどうかを判断する方法は?

で ソースコードを閲覧できますjad --source-only 。

[arthas@3633]$ jad --source-only com.shockang.study.ArthasDemo
       /*
        * Decompiled with CFR.
        */
       package com.shockang.study;

       import com.alibaba.fastjson.JSON;
       import java.util.List;
       import java.util.concurrent.TimeUnit;

       public class ArthasDemo {
           public static void main(String[] args) {
/*15*/         String s = "[{\"name\":\"zhangsan\",\"age\":\"10\",\"telephone\":\"123456\",\"interests\":[\"sing\",\"dance\",\"rap\"]},\n{\"name\":\"lisi\",\"age\":\"20
\",\"telephone\":\"123457\",\"interests\":[\"sing\",\"swim\"]},\n{\"name\":\"wangwu\",\"age\":\"30\",\"telephone\":\"123458\",\"interests\":[\"sing\",\"program\"]}]";
               while (true) {
/*20*/             System.out.println(new ArthasDemo().convert(s));
                   try {
/*22*/                 TimeUnit.SECONDS.sleep(10L);
/*25*/                 continue;
                   }
                   catch (InterruptedException e) {
/*24*/                 e.printStackTrace();
                       continue;
                   }
                   break;
               }
           }

           private List<People> convert(String s) {
/*30*/         return JSON.parseArray(s, People.class);
           }

           private static class People {
               private String name;
               private String age;
               private String telephone;
               private List<String> interests;

               private People() {
               }

               public String toString() {
                   return "ArthasDemo.People(name=" + this.getName() + ", age=" + this.getAge() + ", telephone=" + this.getTelephone() + ", interests=" + this.getIntere
sts() + ")";
               }

               public String getName() {
                   return this.name;
               }

               public void setName(String name) {
                   this.name = name;
               }

               public String getAge() {
                   return this.age;
               }

               public String getTelephone() {
                   return this.telephone;
               }

               public List<String> getInterests() {
                   return this.interests;
               }

               public void setAge(String age) {
                   this.age = age;
               }

               public void setTelephone(String telephone) {
                   this.telephone = telephone;
               }

               public void setInterests(List<String> interests) {
                   this.interests = interests;
               }
           }
       }

[arthas@3633]$

質問 3: 問題が発生し、オンラインでデバッグできない場合、ログを追加することによってのみ再発行できますか?

上記の質問 2 の watch sum コマンドを使用して、メソッド呼び出しの状況を表示できますtt 。

さらに、回線上のコードはredefine コマンドで「ホットリプレース」でき 、アプリケーションの再起動後に無効になります。これは、いくつかの緊急事態で奇跡的な効果をもたらします。

たとえば、メソッド本体のコードを変更して、ログ出力の行を追加してみましょう。

    private List<People> convert(String s) {
        System.out.println(s);
        return JSON.parseArray(s, People.class);
    }

この時点で、実行中の ArthasDemo コードを新しいコードのコンパイル済みクラス ファイルにホット 置換できます。

redefine コマンドは、JVM メモリ (メソッド領域) にロードされたクラスをホット置換します。

この図から、ソース コードに文字列 s を出力するロジックがないことは明らかですが、JVM メモリ (メソッド領域) にロードされたクラスをホット置換したため、コンソールには文字列が出力されます。

質問 4: オンラインで特定のユーザーのデータ処理に問題がありますが、オンラインでデバッグできず、オフラインで再現できません。

この問題の完全な解決策はありません

問題2と問題3の解決策を参照してください

tt コマンドを使用して、コマンドラインから返された結果をファイルに出力し、異常なレコードの行を選択してコマンドを使用して tt -i 詳細な分析を行うことをお勧めします 。

teeこのコマンドは、標準入力デバイスからデータを読み取り、その内容を標準出力デバイスに出力して、ファイルとして保存します。

ティー コマンド

tt -t com.shockang.study.ArthasDemo convert | tee /Users/shockang/Downloads/log

さらに、 monitor コマンド統計メソッドを使用して、呼び出しの成功と失敗を呼び出すこともできます。

監視コマンド

monitor -c 30 com.shockang.study.ArthasDemo convert | tee /Users/shockang/Downloads/log1

-c の後に統計期間が続きます。デフォルト値は 120 秒です。

質問 5: システムの健全性を確認するためのグローバルな視点はありますか?

このコマンドを使用して dashboard 、現在のシステムのリアルタイム データ パネルを表示します。Ali-tomcat で実行している場合、qps、rt、エラー番号、スレッド プール情報など、現在の Tomcat のリアルタイム情報が表示されます。 . HTTP リクエストの。

ダッシュボードのリアルタイム データ パネル

図から、スレッドのステータス、メモリ使用量、システム パラメータなどを確認できます。

質問 6: JVM のリアルタイム実行ステータスを監視する方法はありますか?

コマンドを使用して jvm 、JVM のリアルタイムの実行ステータスを表示します。

JVM のリアルタイム実行ステータス

質問 7: アプリケーションのホット スポットをすばやく見つけて、フレーム グラフを生成するにはどうすればよいですか?

profiler このコマンドは、アプリケーションのホットスポットのフレーム グラフの生成をサポートしています。基本的に、連続サンプリングにより、収集されたサンプリング結果がフレーム グラフに生成されます。

デフォルトでは、cpu のフレーム グラフが生成されます。つまり、イベントは cpu であり、 --event パラメータで指定できます。異なるシステムが異なるイベントをサポートしていることに注意してください

 デフォルトでは、arthas はポート 3658 を使用します。これを開くことができます: http://localhost:3658/arthas-output/ arthas-output ディレクトリでプロファイラーの結果を表示します。

プロファイラ ディレクトリ

アイテムを選択 クリック

プロファイラーの結果グラフ

質問 8: JVM から直接クラスのインスタンスを見つける方法は?

利用すること vmtool で目的を達成できる

この機能は Arthas 3.5.1 で新しく追加されました。公式ドキュメントを参照できます https://arthas.aliyun.com/doc/vmtool.html#id1

$ vmtool --action getInstances --className java.lang.String --limit 10
@String[][
    @String[com/taobao/arthas/core/shell/session/Session],
    @String[com.taobao.arthas.core.shell.session.Session],
    @String[com/taobao/arthas/core/shell/session/Session],
    @String[com/taobao/arthas/core/shell/session/Session],
    @String[com/taobao/arthas/core/shell/session/Session.class],
    @String[com/taobao/arthas/core/shell/session/Session.class],
    @String[com/taobao/arthas/core/shell/session/Session.class],
    @String[com/],
    @String[java/util/concurrent/ConcurrentHashMap$ValueIterator],
    @String[java/util/concurrent/locks/LockSupport],
]

パラメーターを使用して --limit、戻り値の数を制限し、大きなデータを取得するときに JVM への負荷を回避できます。デフォルト値は 10 です。

特定のクラス インスタンスを正確に見つけたい場合は、次のようにクラスローダー名またはクラスローダー ハッシュを指定できます。

vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext
vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext

クラスローダのハッシュの取得方法については、上記の質問 1 を参照してください。

また、メモリが逼迫している一部の実稼働環境で奇跡的な効果を発揮する、 「GC を強制する」ことができる vmtool の優れた機能もあります

vmtool --action forceGc

おすすめ

転載: blog.csdn.net/robinhunan/article/details/128997596