JVMソース分析のjstatツールの原理は完全に解読します。

6881 ワード

概要
jstatはhotspotが持っている道具です。javaと同じJAVA_HOME/binの下にもあります。このツールを通じて、現在のプロセスのgc、compler、class、memoryなどの関連状況をリアルタイムで知ることができます。具体的にはjstat -optionsを通して、どのタイプのデータをサポートしているのか、例えばJDK 8の下の結果を見てみます。
-class-compiler
-gc
-gccapacity
-gccause
-gcmetacapacity
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcutil
-printcompilation
jstatの出力
jstat皆さんがたくさん使っていますが、よく使われているのはjstat-gcutilです。出力は以下の通りです。
~ ᐅ jstat -gcutil 692 1000
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT  0.00  41.49  59.79  83.66  89.92  78.74    295    5.436    10    3.855    9.291
  0.00  41.49  59.80  83.66  89.92  78.74    295    5.436    10    3.855    9.291
  0.00  41.49  59.80  83.66  89.92  78.74    295    5.436    10    3.855    9.291
  0.00  41.49  59.80  83.66  89.92  78.74    295    5.436    10    3.855    9.291
  0.00  41.49  59.80  83.66  89.92  78.74    295    5.436    10    3.855    9.291
各列はどのように定義されていますか?どのように計算されていますか?実はtools.jarにjstat_というファイルがあります。optionsでは、このファイルには上の各タイプの出力結果が定義されています。例えば、gcutilです。
option gcutil {
  column {
    header "^S0^"    /* Survivor 0 Space - Percent Used */
    data (1-((sun.gc.generation.0.space.1.capacity - sun.gc.generation.0.space.1.used)/sun.gc.generation.0.space.1.capacity)) * 100
    scale raw
    align right
    width 6
    format "0.00"
  }
  column {
    header "^S1^"    /* Survivor 1 Space - Percent Used */
    data (1-((sun.gc.generation.0.space.2.capacity - sun.gc.generation.0.space.2.used)/sun.gc.generation.0.space.2.capacity)) * 100
    scale raw
    align right
    width 6
    format "0.00"
  }
  column {
    header "^E^"    /* Eden Space - Percent Used */
    data (1-((sun.gc.generation.0.space.0.capacity - sun.gc.generation.0.space.0.used)/sun.gc.generation.0.space.0.capacity)) * 100
    align right
    scale raw
    width 6
    format "0.00"
  }
  column {
    header "^O^"    /* Old Space - Percent Used */
    data (1-((sun.gc.generation.1.space.0.capacity - sun.gc.generation.1.space.0.used)/sun.gc.generation.1.space.0.capacity)) * 100
    align right
    scale raw
    width 6
    format "0.00"
  }
  column {
    header "^M^"    /* Metaspace Space - Percent Used */
    data (1-((sun.gc.metaspace.capacity - sun.gc.metaspace.used)/sun.gc.metaspace.capacity)) * 100
    align right
    width 6
    scale raw
    format "0.00"
  }
  column {
    header "^CCS^"    /* Compressed Class Space Space - Percent Used */
    data (1-((sun.gc.compressedclassspace.capacity - sun.gc.compressedclassspace.used)/sun.gc.compressedclassspace.capacity)) * 100
    align right
    width 6
    scale raw
    format "0.00"
  }
  column {
    header "^YGC^"    /* Young Generation Collections */
    data sun.gc.collector.0.invocations
    align right
    width 6
    format "0"
  }
  column {
    header "^YGCT^"    /* Young Generation Collection Time */
    data sun.gc.collector.0.time/sun.os.hrt.frequency
    align right
    scale sec
    width 8
    format "0.000"
  }
  column {
    header "^FGC^"    /* Full Collections */
    data sun.gc.collector.1.invocations
    align right
    width 5
    scale raw
    format "0"
  }
  column {
    header "^FGCT^"    /* Full Collection Time */
    data sun.gc.collector.1.time/sun.os.hrt.frequency
    align right
    scale sec
    width 8
    format "0.000"
  }
  column {
    header "^GCT^"    /* Total Garbage Collection Time */
    data (sun.gc.collector.0.time + sun.gc.collector.1.time)/sun.os.hrt.frequency
    align right
    width 8
    scale sec
    format "0.000"
  }
}
上記の定義から私達はgcutilの各列がどういう意味であるかを知っています。sun.gc.generation.0.space.0.capacityのようないくつかの変数はjvmで作成され、リアルタイムで更新された値です。
jstatはどのようにこれらの変数の値を取得しますか?
変数値は明らかにターゲットプロセスから取得されましたが、どうやって来ましたか?local socketですか?それともmemory shareですか?実は共有ファイルから来ました。このファイルはPerfDataといいます。主にtmp/hsperfdata_/このファイル
PerfDataファイル
ファイルの作成
このファイルが存在するかどうかは二つのパラメータ、一つはUsePerfData、もう一つはPerf DisplaleShardMem、もし設定されたら-XX:+Perf DisplaleShardMemまたは-XX:-UsePerfData、このファイルは存在しません。デフォルトではPerfDiscleShardMemはクローズされています。UsePerfDataとPerfDispable SharedMemの二つのパラメータについてここで説明します。
  • UsePerfData:UsePerfDataというパラメータをオフにしたら、jvm起動中にperf memoryは作成されません。jvm運転中にこれらの性能データは保存されません。デフォルトはオープン
  • です。
  • PerfDiscable ShardMem:このパラメータはPerfDataを記憶するメモリが共有できるかどうかを決定しています。つまり、このパラメータ設定が設定されていないにもかかわらず、jvmは起動時には一つのメモリを割り当ててPerfDataを保存します。このPerfDataは他のプロセスで見られる問題ではないです。このパラメータを設定したら、共有できないと説明します。この時、他のプロセスはこのメモリにアクセスできなくなります。例えば、jps、jstatなどは仕事ができません。デフォルトでは、このパラメータは閉じられています。すなわち、デフォルトサポート共有方式
  • 具体的なコードはPerfメモリにあります。memoryレギオンに
     if (PerfDisableSharedMem) {    // do not share the memory for the performance data.
        _start = create_standard_memory(size);
      }  else {
        _start = create_shared_memory(size);    if (_start == NULL) {      // creation of the shared memory region failed, attempt
          // to create a contiguous, non-shared memory region instead.
          //
          if (PrintMiscellaneous && Verbose) {
            warning("Reverting to non-shared PerfMemory region.
    "); } PerfDisableSharedMem = true; _start = create_standard_memory(size); } }
    ファイルの削除
    このファイルはいつ削除されますか?通常、プロセスが終了すると自動的に削除されますが、いくつかの極端な状況では、例えば、Kill-9は、この信号jvmをキャプチャすることはできませんので、直接にプロセスを終了し、いくつかの終わりの作業をしていない場合、プロセスはなくなっていますが、このファイルはまだ存在します。このファイルはずっと残していますか?人為的な削除を待つしかないです。jvmではこのような状況を考慮して、現在のユーザーの次のいずれかのjavaプロセス(例えば、jpsを実行します)が起きた時に判断します。下のプロセスファイルは、プロセスがまだ存在するかどうかを確認し、存在しない場合は直接にファイルを削除し、存在するかどうかを判断する具体的な操作は、実際には、キル-0の信号を送信します。異常があるかどうかを確認します。
    ファイルの更新
    このファイルはmmapによってメモリにマッピングされていますが、jstatは直接DirectByteBufferによってPerfDataから読み込まれていますので、メモリの値が変わったら、jstatから見た値が変化します。メモリの値はいつ変化しますか?つまり、50 msに一度の値を更新すると、ほぼリアルタイムと考えられます。
    PerfDataその他の関連VMパラメータ
  • -XX:PerfData MemorySize:指定/tmp/hsperfdata_下のperfDataファイルのサイズは、デフォルトでは32 KBです。ユーザーがこの値を設定すると、jvmで自動的にOSのpage sizeと整列します。例えば、linuxでpagesizeがデフォルトでは4 KBです。31 KBを設定すると、自動的に32 KB
  • が割り当てられます。
  • -XX:+PerfData SaveToFile:プロセスが終了した時にPerfDataの中のデータを特定のファイルに保存するかどうか、ファイルパスは下記のパラメータで指定されます。そうでなければ、現在のディレクトリの下に
  • -XX:PerfData SaveFile:PerfDataファイルを保存するパス
  • を指定します。
    jstatの穴
    私がしばらく考えていた二つの大きな穴:
  • 回正常なBackground CMS GCを発見した後、FGCの値を2回追加しました。CMSにはinit markとremarkがあり、両方のアプリケーションを一時停止する段階があります。また、oldにgcを行うので、
  • を2回も計算しました。
  • JDK 8におけるメタスペースの使用状況は正確ではありません。例えば、CCSCの値はCommpresed Class Space Capacityを表しています。しかし、この値の計算はreerveの値ではないことが分かりました。だから、metaspaceは実用的で少ないことが分かります。したがって、この場合は、それらの値をjmxで取って計算したほうがいいです。
  • size_t CompressedClassSpaceCounters::capacity() {  return MetaspaceAux::committed_bytes(Metaspace::ClassType);
    }
    
      
    おすすめの読書:
    ビッグデータの中台のKafkaは、いったいどこがいいですか?
    JVMパラメータによる頻繁なCMS GC