1. 概要

コンパイラやランタイムなどの一般的な開発ユーティリティに加えて、各JDKリリースには無数の他のツールが付属しています。 これらのツールのいくつかは、実行中のアプリケーションに関する貴重な洞察を得るのに役立ちます。

この記事では、このようなツールを使用して、特定のJVMインスタンスで使用されるGCアルゴリズムについて詳しく知る方法を説明します。

2. サンプルアプリケーション

この記事全体を通して、非常に単純なアプリケーションを使用します。

public class App {
    public static void main(String[] args) throws IOException {
        System.out.println("Waiting for stdin");
        int read = System.in.read();
        System.out.println("I'm done: " + read);
    }
}

明らかに、このアプリは、標準の入力から何かを受け取るまで待機し、実行を続けます。 この一時停止は、長時間実行されるJVMアプリケーションの動作を模倣するのに役立ちます。

このアプリを使用するには、App.javaファイルをjavacでコンパイルしてから、javaツールを使用して実行する必要があります。

3. JVMプロセスの検索

JVMプロセスで使用されるGCを見つけるには、まず、その特定のJVMインスタンスのプロセスIDを特定する必要があります。次のコマンドでアプリを実行したとします。

>> java App
Waiting for stdin

JDKがインストールされている場合、JVMインスタンスのプロセスIDを見つける最良の方法は、jpsツールを使用することです。 例えば:

>> jps -l
69569 
48347 App
48351 jdk.jcmd/sun.tools.jps.Jps

上に示したように、システム上で実行されている3つのJVMインスタンスがあります。 明らかに、2番目のJVMインスタンス(「アプリ」)の説明は、アプリケーション名と一致します。 したがって、探しているプロセスIDは48347です。

jps に加えて、他の一般的なユーティリティをいつでも使用して、実行中のプロセスを除外できます。 たとえば、procpsパッケージの有名なpsツールも同様に機能します。

>> ps -ef | grep java
502 48347 36213   0  1:28AM ttys037    0:00.28 java App

ただし、 jps の方がはるかに使いやすく、必要なフィルタリングも少なくて済みます。

4. 中古GC

プロセスIDを見つける方法がわかったので、すでに実行されているJVMアプリケーションで使用されているGCアルゴリズムを見つけましょう。

4.1. Java8以前

Java 8を使用している場合は、jmapユーティリティを使用して、ヒープサマリー、ヒープヒストグラムを印刷したり、ヒープダンプを生成したりできます。 GCアルゴリズムを見つけるために、ヒープオプションを次のように使用できます。

>> jmap -heap <pid>

したがって、特定のケースでは、CMSGCを使用しています。

>> jmap -heap 48347 | grep GC
Concurrent Mark-Sweep GC

他のGCアルゴリズムの場合、出力はほぼ同じです。

>> jmap -heap 48347 | grep GC
Parallel GC with 8 thread(s)

4.2. Java 9+: jhsdb jmap

Java 9以降、jhsdb jmapの組み合わせを使用して、JVMヒープに関する情報を出力できます。より具体的には、この特定のコマンドは前のコマンドと同等です。

>> jhsdb jmap --heap --pid <pid>

たとえば、アプリは現在G1GCで実行されています。

>> jhsdb jmap --heap --pid 48347 | grep GC
Garbage-First (G1) GC with 8 thread(s)

4.3. Java 9+: jcmd

最近のJVMでは、jcmdコマンドは非常に用途が広いです。 たとえば、これを使用してヒープに関する一般的な情報を取得できます

>> jcmd <pid> VM.info

したがって、アプリのプロセスIDを渡すと、このJVMインスタンスがシリアルGCを使用していることがわかります。

>> jcmd 48347 VM.info | grep gc
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, compressed oops, serial gc, bsd-amd64)
// omitted

出力はG1またはZGCの場合と同様です。

// ZGC
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, z gc, bsd-amd64)
// G1GC
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, compressed oops, g1 gc, bsd-amd64)

grep の魔法を少し使えば、これらのノイズをすべて削除して、GC名を取得することもできます。

>> jcmd 48347 VM.info | grep -ohE "[^\s^,]+\sgc"
g1 gc

4.4. コマンドライン引数

場合によっては、JVMアプリケーションの起動時にGCアルゴリズムを明示的に指定することがあります。 たとえば、ここではZGCを使用することを選択しています。

>> java -XX:+UseZGC App

このような場合、使用されているGCを見つけるためのはるかに簡単な方法があります。 基本的に、必要なのは、アプリケーションがで実行されたコマンドをなんとかして見つけることだけです。

たとえば、UNIXベースのプラットフォームでは、psコマンドを再度使用できます。

>> ps -p 48347 -o command=
java -XX:+UseZGC App

上記の出力から、JVMがZGCを使用していることは明らかです。 同様に、jcmdコマンドもコマンドライン引数を出力できます。

>> jcmd 48347 VM.flags
84020:
-XX:CICompilerCount=4 -XX:-UseCompressedOops -XX:-UseNUMA -XX:-UseNUMAInterleaving -XX:+UseZGC // omitted

驚くべきことに、上記のように、このコマンドは暗黙的および明示的な引数と調整可能オブジェクトの両方を出力します。 したがって、GCアルゴリズムを明示的に指定しなくても、選択されたデフォルトのアルゴリズムが表示されます。

>> jcmd 48347 VM.flags | grep -ohE '\S*GC\s'
-XX:+UseG1GC

さらに驚くべきことに、これはJava8でも機能します。

>> jcmd 48347 VM.flags | grep -ohE '\S*GC\s'
-XX:+UseParallelGC

5. 結論

この記事では、特定のJVMインスタンスで使用されるGCアルゴリズムを見つけるためのさまざまなアプローチを見てきました。 言及されたアプローチのいくつかは特定のJavaバージョンに関連付けられており、いくつかは移植可能でした。

さらに、常に必要なプロセスIDを見つける方法をいくつか見てきました。