java-recipes

ホーム ガベージコレクション(GC) › GC-02

GC-02: JVM オプション(-Xms/-Xmx/PrintGCDetails)

JVM の起動オプションでメモリサイズを設定し、GC ログを確認する方法を解説します。java.lang.management.ManagementFactory を使った プログラムからの JVM 情報取得も学びましょう。

いつ使うか

  • 本番環境でヒープサイズを適切に設定したいとき(-Xms / -Xmx)
  • GC が頻発していないか、GC の所要時間が長くないかを確認したいとき
  • OOM(OutOfMemoryError)発生時に自動でヒープダンプを取得したいとき
  • アプリケーション内部から現在のメモリ使用状況を監視・ログ出力したいとき

主要な JVM オプション

オプション意味備考
-Xms256m初期ヒープサイズ起動直後のヒープ割当量
-Xmx1g最大ヒープサイズこれを超えると OOM
-XX:+UseG1GCG1GC を使用Java 9+ のデフォルト GC
-XX:+UseZGCZGC を使用Java 15+ 本番対応。超低レイテンシ GC
-Xlog:gc*GC ログ出力(Java 9+)Java 8 の -XX:+PrintGCDetails の後継

サンプルコード

JvmOptionsSample.java
public class JvmOptionsSample {

    public static void main(String[] args) {
        // 現在の JVM ヒープ設定を表示
        Runtime runtime = Runtime.getRuntime();
        System.out.println("=== JVM メモリ情報 ===");
        System.out.println("最大ヒープ (-Xmx): " + (runtime.maxMemory() / (1024 * 1024)) + " MB");
        System.out.println("現在のヒープ (-Xms 相当): " + (runtime.totalMemory() / (1024 * 1024)) + " MB");
        System.out.println("空きヒープ: " + (runtime.freeMemory() / (1024 * 1024)) + " MB");
        System.out.println("CPU コア数: " + runtime.availableProcessors());

        // ManagementFactory で詳細情報を取得
        java.lang.management.MemoryMXBean memBean =
            java.lang.management.ManagementFactory.getMemoryMXBean();
        java.lang.management.MemoryUsage heapUsage = memBean.getHeapMemoryUsage();
        java.lang.management.MemoryUsage nonHeapUsage = memBean.getNonHeapMemoryUsage();

        System.out.println("\n=== ヒープメモリ詳細 ===");
        System.out.println("使用中: " + (heapUsage.getUsed() / (1024 * 1024)) + " MB");
        System.out.println("コミット済み: " + (heapUsage.getCommitted() / (1024 * 1024)) + " MB");
        System.out.println("最大: " + (heapUsage.getMax() / (1024 * 1024)) + " MB");

        System.out.println("\n=== 非ヒープメモリ(クラス定義など)===");
        System.out.println("使用中: " + (nonHeapUsage.getUsed() / (1024 * 1024)) + " MB");

        // GC 情報
        System.out.println("\n=== GC 情報 ===");
        for (java.lang.management.GarbageCollectorMXBean gcBean :
             java.lang.management.ManagementFactory.getGarbageCollectorMXBeans()) {
            System.out.println("GC 名: " + gcBean.getName());
            System.out.println("  回数: " + gcBean.getCollectionCount());
            System.out.println("  累計時間: " + gcBean.getCollectionTime() + " ms");
        }

        System.out.println("\n=== よく使う JVM オプション ===");
        System.out.println("-Xms256m      : 初期ヒープサイズ 256MB");
        System.out.println("-Xmx1g        : 最大ヒープサイズ 1GB");
        System.out.println("-XX:+UseG1GC  : G1GC を使用(Java 9+ デフォルト)");
        System.out.println("-XX:+PrintGCDetails : GC の詳細ログ出力");
        System.out.println("-XX:+HeapDumpOnOutOfMemoryError : OOM 時にヒープダンプ");
    }
}

java.lang.management パッケージの ManagementFactory を使うと、GC の回数や累計時間などより詳細な JVM 情報を取得できます。-XX:+PrintGCDetails は Java 9 以降は -Xlog:gc* に変わっています。

よくあるミス・注意点

-Xms と -Xmx を同じ値にするとメモリ拡張のオーバーヘッドが減る

JVM はヒープが不足すると OS からメモリを追加確保します。この操作はコストがかかります。 本番環境では -Xms -Xmx を同じ値に設定することで、 起動時に必要なメモリを確保し、拡張のオーバーヘッドを省けます。

-Xmx を大きくしすぎると Full GC の停止時間が増える

ヒープを大きくするとオブジェクトが長く生き残り、Full GC 発生時に膨大なオブジェクトを 一度に処理することになります。停止時間(STW: Stop-The-World)が長くなるため、 G1GC や ZGC のような低レイテンシ GC の採用もあわせて検討しましょう。

-XX:+PrintGCDetails は Java 9 以降で廃止

Java 9 から GC ログのオプションが統一形式(Unified JVM Logging)に変わりました。 Java 9 以降では -Xlog:gc* を使用してください。-XX:+PrintGCDetails は警告が出るか無視されます。

テストする観点

  • MemoryMXBean.getHeapMemoryUsage().getMax() が -Xmx で指定した値と一致すること
  • ManagementFactory.getGarbageCollectorMXBeans() が 空でないこと(1つ以上の GC が存在すること)
  • 大量のオブジェクト生成後に GC 回数が増加すること
  • -Xmx を小さく設定したとき、大量アロケーションで OutOfMemoryError が発生すること(境界値テスト)

GitHub でソースコードを見る →