概要

JVM オプションは Java アプリケーションの性能とメモリ管理を左右する重要な設定ですが、種類が多く、どこから手をつけてよいか迷いがちです。開発環境では気にならなくても、本番環境でヒープが足りなくなったり GC の停止時間が問題になったりして初めて調べることも多いでしょう。この記事では、ヒープサイズ(-Xms / -Xmx)、GC アルゴリズムの選択(G1GC / ZGC)、GC ログの出力設定、OOM 時のヒープダンプ取得といった、実務で最も使用頻度の高い JVM オプションに絞って整理します。ManagementFactory API を使って実行中の JVM から設定値を取得・確認する方法も合わせて示します。

使いどころ

本番デプロイ前に -Xmx / -Xms を適切に設定し、ヒープ不足による OOM を予防する

GC ログ(-Xlog:gc*)を有効にして、性能劣化時に Full GC の発生状況を事後分析する

OOM 発生時にヒープダンプを自動取得し、Eclipse MAT でリーク箇所を特定する

コード例

JvmOptionsDemo.java
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;

public class JvmOptionsDemo {

    public static void main(String[] args) {
        var runtime = Runtime.getRuntime();

        // ヒープメモリ情報
        System.out.println("=== JVM メモリ情報 ===");
        System.out.println("最大ヒープ (-Xmx)  : "
            + (runtime.maxMemory() / (1024 * 1024)) + " MB");
        System.out.println("現在のヒープ       : "
            + (runtime.totalMemory() / (1024 * 1024)) + " MB");
        System.out.println("空きヒープ         : "
            + (runtime.freeMemory() / (1024 * 1024)) + " MB");
        System.out.println("CPU コア数         : "
            + runtime.availableProcessors());

        // ManagementFactory で詳細情報を取得
        var memBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heap = memBean.getHeapMemoryUsage();
        MemoryUsage nonHeap = memBean.getNonHeapMemoryUsage();

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

        System.out.println("\n=== 非ヒープメモリ ===");
        System.out.println("使用中: " + (nonHeap.getUsed() / (1024 * 1024)) + " MB");

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

        // よく使う JVM オプション一覧
        var options = """
            === よく使う JVM オプション ===
            -Xms256m                        : 初期ヒープサイズ
            -Xmx1g                          : 最大ヒープサイズ
            -XX:+UseG1GC                    : G1GC(Java 9+ デフォルト)
            -XX:+UseZGC                     : ZGC(Java 15+ 低レイテンシ)
            -Xlog:gc*                       : GC ログ出力(Java 9+)
            -XX:+HeapDumpOnOutOfMemoryError : OOM 時ヒープダンプ
            -XX:HeapDumpPath=/tmp/heap.hprof: ダンプ出力先
            """;
        System.out.println(options);
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

デフォルト GC は G1GC。GC ログは -Xlog:gc* に統一。var と import 文の整理で ManagementFactory 周りのコードが読みやすくなる。ZGC が本番利用可能。

Java 17
// Java 17: import + var で簡潔に
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
var runtime = Runtime.getRuntime();
var memBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memBean.getHeapMemoryUsage();
System.out.println("使用中: " + (heapUsage.getUsed() / (1024 * 1024)) + " MB");
// GC ログ: -Xlog:gc*
// デフォルト GC: G1GC

Library Comparison

標準 API(Runtime / ManagementFactory)実行中の JVM からヒープ使用量や GC 回数を確認したいとき。外部依存なしですぐに使える。履歴の蓄積やアラート通知には対応していない。モニタリングツールとの併用が前提になる。
Prometheus + JMX ExporterJVM メトリクスを時系列で収集し、Grafana でダッシュボード化したいとき。Prometheus / Grafana のインフラ構築が必要。小規模プロジェクトではオーバースペック。
Spring Boot ActuatorSpring Boot アプリケーションでメトリクスエンドポイントを手軽に公開したいとき。Spring Boot が前提。Pure Java プロジェクトでは使えない。

注意点

-Xmx を物理メモリの上限近くまで設定すると、OS やほかのプロセスのメモリが不足してスワップが発生し、かえって性能が悪化する

-Xms と -Xmx を同じ値にするとヒープの拡張・縮小が起きないため GC の負荷が安定するが、メモリを常時専有するのでコンテナ環境では注意が必要

Java 8 の -XX:+PrintGCDetails は Java 9 以降で廃止され、-Xlog:gc* に置き換わった。バージョンを跨ぐ運用ではオプションの互換性を確認すること

ZGC は低レイテンシに優れるが、スループット重視の大量バッチ処理では G1GC のほうが適する場合がある。ワークロードに応じた選択が必要

FAQ

-Xms と -Xmx は同じ値にすべきですか。

ヒープの拡張・縮小による GC 負荷を避けたい場合は同じ値が有効です。ただし、コンテナ環境ではメモリ上限との兼ね合いを考慮し、余裕を持たせた設定が安全です。

G1GC と ZGC はどう選びますか。

レスポンスタイムの安定性が求められる API サーバーには ZGC(Java 15+)が適しています。スループット重視のバッチ処理では G1GC のほうが総処理時間が短くなる場合があります。

本番で GC ログを有効にしても性能に影響はありませんか。

GC ログの出力は非常に軽量で、本番環境でも常時有効にしておくのが一般的です。問題発生時に遡って分析できるため、有効にしない理由は基本的にありません。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます