1. ホーム
  2. JVM

Arthas 公式ドキュメントを読んでも使い方がわからない?ぜひご来店の上、学んでください

2022-02-26 03:50:16
<パス

前文

この記事は、Java仮想マシンを修正するための列&quot;100質問に属している&quot;、列は著者のオリジナルです、ソースを引用してください、欠点やエラーは、コメント欄で指摘するのに役立ちます、ありがとうございました!この記事は、Java仮想マシンを修正するための列に属しています。

<ブロッククオート

このコラムの目次と引用は、以下のサイトで見ることができます。 Java仮想マシンを修正するための100の質問

引用元

Arthas は、Alibabaが提供するオープンソースのJava診断ツールで、開発者の間で人気があります。

以下のような問題に遭遇し、途方に暮れているとき。 Arthas が解決の手助けをします。

  1. このクラスはどのjarパッケージからロードされていますか?なぜこのクラスは様々なクラス関連のExceptionを報告するのでしょうか?
  2. 私が変更したコードが実行されないのはなぜですか?間違ったブランチをコミットしてしまったからでしょうか?
  3. 問題が発生したときにオンラインでデバッグできないのですが、ログを追加して再公開するしかないのでしょうか?
  4. オンラインでユーザーからのデータ処理がうまくいかないのですが、オンラインでデバッグできず、オフラインでも再現できないのですが!?
  5. システムで何が起こっているのか、グローバルに把握することはできますか? JVMのライブ実行状態をモニターする方法はありますか?
  6. アプリケーションのホットスポットを迅速に特定し、フレームマップを生成するにはどうすればよいですか?
  7. JVM内部から直接クラスのインスタンスを見つけるにはどうしたらいいですか?

を開いたシャオミンは Artha の公式ドキュメントを公開しました。 Arthas ユーザドキュメント - Arthas 3.5.2 ドキュメント (aliyun.com)

最初に表示される段落はこれです

それでは、気合を入れてドキュメントを読み始めましょう

一通り読んでみて、明は上記の問題に対する公式な標準解がないことに気がつきました

これは埋もれずに殺すことではないのか!!!!

全文

以下、これら8つの問題点について、私が長年Arthasを使ってきた経験をもとに詳しく解決していきますので、疑問点があればコメント欄でご指摘ください。

準備

まず、テストコードを書いてみましょう。

package com.shockang.study;

import com.alibaba.fastjson;
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\",\"phone\":\"123456 \",\"interests\":[\"sing\",\"dance\",\"rap\"]},\n" +
                "{\"name\":\"lisi\",\"age\":\"20\",\"phone\":\"123457\",\" quot;interests\":[\"sing\",\"swim\"]},\n" +
                "{\"name\":\"wangwu\",\"age\":\"30\",\"phone\":\"123458\",\" quot;interests\":[\"sing\",\"program\"]}]";
        // Simulate the process of calling the method over and over again
        for (; ; ) {
            System.out.println(new ArthasDemo().convert(s));
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

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


    @Getter
    @Setter
    @ToString
    @FieldDefaults(level = AccessLevel.PRIVATE)
    private static class People {
        /**
         * Name
         */
        String name;
        /**
         * Age
         */
        String age;
        /**
         * Phone number
         */
        String telephone;
        /**
         * List of interests
         */
        List
 interests;
    }
}

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


コンソールに正常に表示されるのは次のとおりです。

sc

Arthas をダウンロードして実行し、アタッチする Java プロセスを選択します。

アタッチ 成功後、Google Chromeを開き、次のように入力します。 http://127.0.0.1:3658/ WebConsoleを開く

(ネタバレ注意:Mac OSのSafariブラウザは対応していません)

<ブロッククオート

WebConsoleを使う上で一番便利なのは、複数のタブを同時に開けることです

質問1:このクラスは、どのjarパッケージから読み込まれますか?なぜこのクラスは様々なクラス関連のExceptionを報告するのでしょうか?

これは、様々な 依存関係の衝突 全く同じ名前のクラスがあり、そのクラスがどの jar パッケージからロードされているかを通常の方法で正確に解決する方法がない場合です。

心配しないでください、以下の解決策をご覧ください。

  1. sc

By sc -d コマンドを使用して、キーワードを含むクラスが現在JVMにロードされているかどうかをファジーに確認し、そのフルネームを取得します。

<ブロッククオート

を使用することに注意してください。 sc -d *ArthasDemo* コマンドを使用して classLoaderHash を取得します。この値は後で必要となるものです。

classloader

<イグ

  1. クラスローダー

を渡します。 cls クラスファイルがどのjarパッケージから来たかを確認するため

を使用します。 classloader -c コマンドを実行すると、コマンドラインがクリアされます。これは、公式ドキュメントでも実際には見つけることができない、簡単なコマンドです。

<ブロッククオート

注意点 [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. には、上記の最初のステップで取得した Hash 値が入ります。また、クラスファイルのパスは '/' で分割され、末尾が .class である必要があります。

$ 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


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

watch

質問2:なぜ私が変更したコードは実行されないのでしょうか?コミットしていないので、ブランチが間違っているからでしょうか?

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

これらのコマンドはどちらもメソッドの呼び出しプロセスを表示するために使用されますが、その違いは tt コマンドは一度だけ呼び出され、メソッドの呼び出しを一度だけ表示するのに対して watch コマンドは、呼び出しのリストを増やしていき、そのうちの1つを指定して観察することができます。

  1. を使用します。 watch com.shockang.study.ArthasDemo convert "{params,target,returnObj}" -f -x 4 watch コマンドでメソッドコールを確認できます。ArthasDemoクラス内の変換メソッド呼び出しを見たいと思います。

tt

tt に続いて、完全なクラス名とメソッド名、および OGNL 式を指定し、-f で正常な戻り値と異常な戻り値のどちらかを監視し、-x で出力のプロパティを繰り返し処理します(デフォルトは 1 です)。

<ブロッククオート

私の経験上、最大探索深度は4と書くことが推奨されており、それ以上はサポートされていない

  1. 使用方法 watch コマンドを使用してメソッドコールを監視する場合 tt -t com.shockang.study.ArthasDemo convert コマンドを使用すると を複数回呼び出す。 で、そのうちの一つを選択して観察することができますが、複数のレイヤーに入れ子になっている場合は出力を見る方法がないのに対して jad --source-only を見ることができます。 多階層ネスト の結果が得られます。

tt -t を使用して、現在のメソッドの各呼び出しの環境サイトを記録します。

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

       import com.alibaba.fastjson;
       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\",\"phone\":\" 123456\\quot;,\"interests\quot;:[\"sing\quot;,\"dance\quot;,\"rap\quot;]},\n{\"name\quot;:\" 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]$



TIMESTAMPはメソッド呼び出しの発生時刻、COSTは呼び出しにかかった時間(ms)、IS-RETは正常に戻ったか、IS-EXPは異常に戻ったか、OBJECTはオブジェクトのHASH値であることを示しています。

<ブロッククオート

特定のタイムスライスの情報については、-iパラメータの後に対応するINDEX番号を付けて詳細を見ることができる

<ブロッククオート

グラフが興味のある事柄のリストを表示するのは、そのtoStringメソッドを呼び出すためで、java.lang.ComponentのtoStringメソッドではハッシュ値しか見ることができません。

  1. コードがコミットされたかどうかを確認するにはどうすればよいですか?

を使用することで watch ソースコードを閲覧することができます。

tt

質問3:問題が発生してもオンラインでデバッグできないのですが、ログを追加して再公開すればいいのでしょうか?

を使って redefine private List convert(String s) { System.out.println(s); return JSON.parseArray(s, People.class); } コマンドでメソッドの呼び出しを確認できます。

また、メソッド呼び出しは tt コマンド ホットリプレイス のコードで、アプリが再起動した後に失敗することに注意してください。これは、緊急事態には素晴らしい効果を発揮します。

メソッド本体内のコードを修正して、ログ印刷の行を追加するとします。

tt -i

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

この画像から明らかなように、ソースコードには文字列sを表示するロジックはありませんが、JVMのメモリ(メソッド領域)にロードされたクラスをホットプレースしたため、コンソールにはとにかく文字列が表示されます。

問題4:あるユーザーのデータ処理にオンラインで問題が発生したが、やはりオンラインでデバッグできないし、オフラインでも再現できない!

この問題に対する完璧な解決策はない

解答は問題2、問題3を参照

を使用することをお勧めします。 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


あるいは dashboard コマンドを使って、メソッド呼び出しの成功と失敗を数えることができます。

jvm

-c の後に統計期間(デフォルトは120秒)を指定します。

質問5:システムのパフォーマンスを確認するためのグローバルビューはありますか?

を使用します。 profiler コマンドを使用すると、現在のシステムのライブ・データ・パネルである
Ali-tomcatで実行した場合、HTTPリクエストのqps、rt、エラー数、スレッドプール情報など、現在のtomcatに関する情報をリアルタイムに表示します。

グラフから、スレッド数、メモリ使用量、システムパラメータなどを確認することができます。

質問6:JVMの実行状態をリアルタイムに監視する最も良い方法は何ですか?

を使用することで vmtool コマンドを使用して、JVMのライブステータスを確認します。

<イグ

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

$ 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,] @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], ] コマンドは、アプリケーションのホットスポットに対応したフレームマップの生成に対応しています。基本的には、連続的にサンプリングし、収集したサンプルからフレームマップを生成することによって行われます。

<ブロッククオート

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


デフォルトでは、3658番ポートを使用するartasが開きます。 http://localhost:3658/arthas-output/ プロファイラの結果をarthas-outputディレクトリの下に表示する場合。

項目を選択し、クリック

質問8:JVM内から直接クラスのインスタンスを見つけるにはどうしたらいいですか?

を使用します。 --limit 目標を達成することができます。

この機能は Arthas 3.5.1 で追加されました。の公式ドキュメントを参照してください。 vmtool - Arthas 3.5.2 ドキュメント (aliyun.com)

vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework. ApplicationContext

vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext


通過する vmtool --action forceGc パラメータを使用すると、返される値の数を制限して、非常に大きなデータを取得する際にJVMに負担をかけないようにすることができます。デフォルト値は10です。

特定のクラスインスタンスをピンポイントで指定したい場合は、以下のようにクラスローダー名やクラスローダー・ハッシュを指定することで可能です。

vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework. ApplicationContext


vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext


<ブロッククオート

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

vmtoolには、以下のような素敵な機能もあります。 GCを強制する これは、メモリが逼迫している実稼働環境において、素晴らしい効果を発揮することができます。

vmtool --action forceGc