SkyWalking で JVM を可観測するベストプラクティス¶
はじめに¶
JVM はコンピューティングデバイス向けの仕様であり、仮想的なコンピュータです。Java Virtual Machine の略称です。Java は高度に抽象化された言語で、自動メモリ管理などの機能を備えているため、JVM という抽象レイヤーが存在します。JVM はオペレーティングシステム上で動作し、Java バイトコードを実行することで、Java のクロスプラットフォームを実現します。
ここでは、JVM のメモリ構造とスレッドについて簡単に説明し、その後 SkyWalking を使用して JVM メトリクスデータを収集し、Guance で可観測性を実現する方法を紹介します。
JVM メモリ構造¶
Java コンパイラは JVM のみを対象として、JVM が理解できるバイトコードファイルを生成します。JVM 内のクラスローダーがバイトコードをロードし、ロードが完了すると JVM 実行エンジンが実行します。クラスのロードプロセス全体で、JVM はデータや関連情報を格納するためにメモリ領域を使用します。この領域が一般的に JVM メモリと呼ばれるものです。
JVM 仕様によると、JVM メモリは次のように分類されます。
-
プログラムカウンタ
次の JVM 命令の実行アドレスを記録します。 -
Java 仮想マシンスタック
各スタックは複数のスタックフレーム(Frame)で構成され、メソッド呼び出しごとに占有されるメモリに対応し、ローカル変数テーブル、オペランドスタック、定数プール参照などを格納します。メソッド呼び出しから実行完了まで、Java 仮想マシンスタック内でのスタックフレームのプッシュとポップに対応します。-Xss で仮想マシンスタックのメモリサイズを設定できます。 -
ネイティブメソッドスタック
機能と特性は Java 仮想マシンスタックと似ていますが、ネイティブメソッド用にサービスを提供します。 -
メソッド領域
メソッド領域は JVM 仕様であり、PermGen(パーマネント世代)と Metaspace(メタスペース)はその実装方法の一つです。JDK8 以降、元の PermGen データはヒープと Metaspace に分割されました。Metaspace はクラスのメタ情報を格納し、静的変数や文字列定数プールなどはヒープに格納されます。 -
ヒープ
ヒープ領域はすべてのスレッドで共有され、主にオブジェクトインスタンスと配列を格納します。物理的に連続していない領域に配置することも可能ですが、論理的には連続している必要があります。
ヒープメモリは Young Generation(若い世代)と Old Generation(古い世代)に分けられます。Young Generation はさらに Eden 領域と Survivor 領域に分かれます。Survivor 領域は FromSpace と ToSpace で構成されます。Eden 領域が大部分を占め、Survivor の 2 つの領域は小さな容量で、デフォルトの比率は 8:1:1 です。
Java ヒープでインスタンスの割り当てに十分なメモリがなく、ヒープを拡張できない場合、Java 仮想マシンは OutOfMemoryError 例外をスローします。 -
実行時定数プール
実行時定数プールはメソッド領域の一部であり、テーブルです。仮想マシン命令はこのテーブルに基づいて、実行するクラス名、メソッド名、パラメータ型などの情報を見つけます。 -
ダイレクトメモリ
ダイレクトメモリは仮想マシンの実行時データ領域の一部ではなく、Java 仮想マシン仕様で定義されたメモリ領域でもありません。Java ヒープサイズの制限を受けず、ネイティブマシンの総メモリサイズの制限を受けます。ダイレクトメモリは -XX:MaxDirectMemorySize で指定することもできます。ダイレクトメモリの割り当ては通常のヒープメモリよりもパフォーマンスコストが高くなりますが、IO の読み書き性能は通常のヒープメモリよりも優れています。メモリが不足すると OutOfMemoryError 例外がスローされます。
Thread¶
Java にはユーザースレッド(User Thread)とデーモンスレッド(Daemon Thread)の 2 種類のスレッドがあります。
- ユーザースレッドはシステムのワーカースレッドと見なすことができ、アプリケーションが実行する必要のあるビジネス操作を実行します。
- デーモンスレッドは特殊なスレッドで、バックグラウンドでガベージコレクションスレッドなどのシステムサービスを静かに実行します。
JVM におけるスレッドの状態は次のとおりです。
- NEW(新規状態):実行を開始していないスレッドの状態。
- RUNNABLE(実行可能状態):実行中のスレッド状態。
- BLOCKED(ブロック状態):モニターロックを待機しているために待機状態。
- WAITING(待機状態):別のスレッドが特定の操作を実行するのを無期限に待機。
- TIMED_WAITING(時間指定待機状態):別のスレッドが特定の操作を実行するのを時間制限付きで待機。
- TERMINATED(終了状態):スレッドが終了状態。
性能メトリクス¶
| メトリクス | 説明 | データ型 | 単位 |
|---|---|---|---|
class_loaded_count |
ロードされたクラス数。 | int | count |
class_total_loaded_count |
ロードされたクラスの総数。 | int | count |
class_total_unloaded_class_count |
アンロードされたクラスの総数。 | int | count |
cpu_usage_percent |
CPU 使用率のパーセンタイル。 | float | percent |
gc_phrase_old/new_count |
GC の Old または New の回数。 | int | count |
heap/stack_committed |
ヒープまたはスタックのコミット済みメモリ量。 | int | count |
heap/stack_init |
ヒープまたはスタックの初期化済みメモリ量。 | int | count |
heap/stack_max |
ヒープまたはスタックの最大メモリ量。 | int | count |
heap/stack_used |
ヒープまたはスタックの使用済みメモリ量。 | int | count |
pool_*_committed |
各種プールのコミット済みメモリ量 (code_cache_usage、newgen_usage、oldgen_usage、 survivor_usage、permgen_usage、metaspace_usage)。 |
int | count |
pool_*_init |
各種プールの初期化済みメモリ量 (code_cache_usage、newgen_usage、oldgen_usage、 survivor_usage、permgen_usage、metaspace_usage)。 |
int | count |
pool_*_max |
各種プールの最大メモリ量 (code_cache_usage、newgen_usage、oldgen_usage、 survivor_usage、permgen_usage、metaspace_usage)。 |
int | count |
pool_*_used |
各種プールの使用済みメモリ量 (code_cache_usage、newgen_usage、oldgen_usage、 survivor_usage、permgen_usage、metaspace_usage)。 |
int | count |
thread_blocked_state_count |
ブロック状態のスレッド数。 | int | count |
thread_daemon_count |
デーモンスレッド数。 | int | count |
thread_live_count |
生存スレッド数。 | int | count |
thread_peak_count |
ピークスレッド数。 | int | count |
thread_runnable_state_count |
実行可能状態のスレッド数。 | int | count |
thread_time_waiting_state_count |
時間指定待機状態のスレッド数。 | int | count |
thread_waiting_state_count |
待機状態のスレッド数。 | int | count |
前提条件¶
- DataKit 1.4.17 以上がインストールされていること。
操作手順¶
1 収集の有効化¶
DataKit がインストールされているホストにログインし、サンプルファイルをコピーします。
DataKit と JVM の Jar が 同じホスト上にない 場合は、skywalking.conf の address = "localhost:11800" の localhost を Jar が存在するホストの IP に変更する必要があります。
今回使用する Jar と DataKit は同じホスト上にあるため、localhost で JVM メトリクスを報告できるので、サンプルファイルを変更する必要はありません。
2 アプリケーションのデプロイ¶
skywalking-demo プロジェクトをダウンロードし、Idea で開いて「package」をクリックすると、skywalking-user-service.jar ファイルが生成されます。
Skywalking をダウンロードし、解凍して agent ディレクトリをホストにコピーし、skywalking-user-service.jar と同じディレクトリに配置します。
以下のコマンドを実行してアプリケーションを起動します。
java -javaagent:agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=skywalking-user \
-Dskywalking.collector.backend_service=localhost:11800 \
-jar skywalking-user-service.jar
3 JVM の可観測性¶
「Guance」にログインし、「シナリオ」を選択します。入力欄に「JVM」と入力し、「JVM Skywalking モニタリングビュー」を選択して「確定」をクリックします。
新しく作成された「JVM Skywalking モニタリングビュー」をクリックすると、観測を開始できます。


