JVM主要オプション


コンパイラフラグ

JVMチューニングで最初に検討するのがコンパイラの選択となる。

ただ、近年は階層的コンパイルが有効になっていたり、プラットフォーム別で適した設定がデフォルトで有効になっていることもあり、改めて設定するケースは少ない。

フラグ 説明
-client クライアントコンパイラ(C1)を使用する
-server サーバコンパイラ(C2)を使用する
-server -XX:+TieredCompilation 階層的コンパイルを使用する

コンパイラは、クライアントコンパイラ(C1)、サーバコンパイラ(C2)の2種類があり、JVM起動オプションで指定する。

C1コンパイラはデスクトップアプリケーションのように起動時の速度が重要な場合に、早期にJIT(just-in-time)コンパイルを開始する。

一方C2コンパイラはコードの振る舞いを十分に解析した後にコンパイルを開始する。これによりより効果的な最適化が実現できる。

大雑把に言うと以下となる

  • C1:コンパイルが早く生成されるコードが遅い
    1. インタプリタ 実行しながらHotなメソッドを検出
    2. ネイティブ Hotと判定したメソッドをJITコンパイルして実行する
  • C2:コンパイルが遅いが生成されるコードが早い
    1. インタプリタ 実行しながらプロファイリングする
    2. ネイティブ Hotと判定したメソッドをプロファイリングデータを使ってJITコンパイルして実行する

C2はプロファイリングデータを必要とするためインタプリタのフェーズが長く、アプリケーションの起動直後はパフォーマンスが出ない。 これを解消するために -XX:+TieredCompilation オプションがあり、起動早期からコンパイルを行い、サーバコンパイラによりホットな部分を改めて(より最適化された)コンパイルを行うことができる。これはJava7u4以降で実用に達し、Java8ではデフォルトで有効化されている。

Java8 では階層的コンパイルを選択すれば良いが、コードキャッシュ(-XX:ReservedCodeCacheSize=N にて指定)が多く必要となるので注意が必要。 コードキャッシュのサイズが不足した場合、(コードキャッシュが空くまで)JITコンパイルが停止し、インタプリタモードで動作するため、パフォーマンス劣化の原因となる場合がある。これについては後述する。


GC戦略

GC戦略はアプリケーションの要件と照らして適切なものを選択する必要がある。

フラグ 説明
-XX:+UseSerialGC シリアル型GC。マイナーGC、フルGCの両方がアプリケーションを停止して処理される
-XX:+UseParallelGC -XX:+UseParallelOldGC パラレル型GC。young領域とold領域のGCに複数スレッドが利用される。マイナーGC、フルGCの両方でアプリケーションスレッドは停止
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC CMS型GC。マイナーGCではアプリケーションスレッドを停止し、複数スレッドで処理。old領域はアプリケーションスレッドと並行でGCが行われる
-XX:+UseG1GC CMS型GCを複数のリージョンに分割したヒープ上で行う。CMS型GCに比べてold領域の断片化が発生しにくい
  • シリアルGC

    • 最もシンプルなGC
    • 単一CPUの32bitプラットフォームのデフォルト
    • GC処理は常にアプリケーションスレッドが停止
    • 小さなヒープ(100M程度)の場合の選択肢
  • パラレルGC

    • GC処理は常にアプリケーションスレッドが停止
    • マイナーGCを複数スレッドで処理
    • -XX:+UseParallelOldGC フラグによりフルGCも複数スレッドで処理
    • GCによる長いストップ・ザ・ワールドが許容でき、スループットを最大化する場合の選択肢
  • CMSGC

    • マイナーGC時にはアプリケーションスレッドが停止
    • マイナーGCは複数スレッドで処理(-XX:+UseParNewGCのアルゴリズム利用)
    • old領域のGC時にはアプリケーションスレッドが停止しない
      • バックグラウンドスレッドにより並列で未使用オブジェクトを破棄
      • old領域が断片化が進行した場合、アプリケーションスレッドを停止してクリーンアップとコンパクト化を単一スレッドで実施
    • 同種の -XX:+CMSIncrementalMode(細かい単位でGCを行う) は今では推奨されていない
  • G1GC

    • マイナーGC時にはアプリケーションスレッドが停止
    • マイナーGCは複数スレッドで処理
    • old領域のGC時にはアプリケーションスレッドが停止しない
      • バックグラウンドスレッドにより並列で未使用オブジェクトを破棄
    • ヒープはリージョンに分割されており、断片化が発生しにくい
    • 大きなヒープ(4G以上)でアプリケーションの停止を望まず、CPUに余裕のある場合の選択肢


GCログ

GCログは商用利用であれば必ず設定しておきたい。

フラグ 説明
-verbose:gc GCログを出力
-Xloggc:<パス> GCログをファイル出力 Java8からは gc_%p_%t.log のようにすると、%pにPID, %tに日時が入ったファイルが作成できる
-XX:+PrintGCDetails GCの詳細出力
-XX:+PrintGCDateStamps GCログに日時出力する。-XX:+PrintGCTimeStamps だと相対時間となり見にくい
-XX:+PrintGCApplicationStoppedTime Stop The World した時間を出力
-XX:+UseGCLogFileRotaiton GCログのログローテートを有効化
-XX:+NumberOfGCLogFiles=N GCログの世代保持数
-XX:+GCLogFileSize=N GCログのログローテートの閾値 10Mのように指定


ヒープ関連

フラグ 説明
-XX:+PrintClassHistogram スレッドダンプ取得時にヒープ統計情報を出力
-XX:+HeapDumpOnOutOfMemoryError OutOfMemoryError発生時にヒープダンプを出力


設定しておきたい Java 起動オプション

フラグ 説明
-server C2を有効化
-Xms 初期ヒープサイズ。 -Xmxと同値にしたい
-Xmx 最大ヒープサイズ
-Xmn(-XX:NewSize -XX:MaxNewSize) New領域サイズ
-XX:PermSize Perm領域初期サイズ。-XX:MaxPermSizeと同値にしたい(~Java7)
-XX:MaxPermSize Perm領域最大サイズ(~Java7)
-XX:MetaspaceSize メタスペースのGCを実施する目安サイズ指定(Java8~)
-XX:MaxMetaspaceSize メタスペースの最大サイズを指定する(Java8~)
-XX:ReservedCodeCacheSize コードキャッシュサイズ
-Xloggc:<パス> GCログをファイル出力
-XX:+PrintGCDetails GCの詳細出力
-XX:+PrintGCTimeStamps GCログにタイムスタンプ出力
-XX:+PrintGCDateStamps GCログに日時出力
-XX:+PrintGCApplicationStoppedTime Stop The World した時間を出力
-XX:+UseGCLogFileRotaiton GCログのログローテートを有効化
-XX:+NumberOfGCLogFiles=N GCログの世代保持数
-XX:+GCLogFileSize=N GCログのログローテートの閾値
-XX:+PrintClassHistogram ヒープ統計出力用
-XX:+HeapDumpOnOutOfMemoryError OutOfMemoryError時にヒープダンプ出力


  • MaxMetaspaceSize のデフォルトは超でかいので、メモリリークの予防の意味で設定しておきたい
  • -XX:ReservedCodeCacheSize 近年動的なクラス生成などで必要量が多くなる傾向があり、コードキャッシュが足りなくなるとJITコンパイラが停止して突然のパフォーマンス劣化になるので注意して設定
    • -XX:+PrintCodeCache(VM終了時にコードキャッシュ情報を出力) や -XX:+PrintCodeCacheOnCompilation(JITコンパイル時にコードキャッシュ情報を出力)なども使い見極めた上で設定したい
    • -XX:+PrintCodeCache(VM終了時にコードキャッシュ情報を出力) や -XX:+PrintCodeCacheOnCompilation(JITコンパイ
  • GC戦略は、CMSGC か、超潤沢なメモリを使える場合はG1GCが選択子になり、CMSGC の場合は例えば以下ぐらいでまずはいいと思う(だいたいデフォルトで有効だけど)
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
-XX:+CMSClassUnloadingEnabled
  • -XX:SurvivorRatio-XX:MaxTenuringThreshold-XX:TargetSurvivorRatio などはチューニング候補になるが、最近の Java であればプラットフォーム別のデフォルト値のままでも下手にチューニングするより良いと思う。
    • 昔のデフォルト '-XX:MaxTenuringThreshold=32' は Java8 では 0~15 でないと起動できないなどあるしネ
  • -XX:+CMSClassUnloadingEnabled はクラスアンロードを有効にするのでPermを十分にとった上で有効にするのもいいと思う
  • -XX:+UseCMSInitiatingOccupancyOnly-XX:CMSInitiatingOccupancyFraction=N は状況に応じてだけど、いらないかな

まとめ

今は下手にチューニングするならデフォルトの方が良いと思います。



Javaパフォーマンス

Javaパフォーマンス

Javaパフォーマンスチューニング 第2版

Javaパフォーマンスチューニング 第2版