G1GC 利用時にはJVMオプション -XX:+ExplicitGCInvokesConcurrent を指定しておきたい


System.gc() 呼び出し

System.gc() を呼び出すと、デフォルトで Stop The World の Full GC を実行します。

System.gc() なんて使っていない。という場合でも Java 自体の内部で呼び出される場合があります。

たとえば sun.rmi.transport.GC の内部では System.gc() を呼び出しており、RMI利用時に、RMI GC Daemon というスレッドから、デフォルトで1時間毎に System.gc() が行われます(RMIの分散ガーベッジコレクション)(昔は1分間隔で動作していた)。

RMI は明示的に利用していなくとも、アプリケーションサーバで有効化されていることが多いので注意が必要です。

分散ガーベッジコレクションの発生間隔は以下の JVMオプションで変更することができます(ミリ秒単位の数値を指定)。

-Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000

他にも java.nio.Bitssun.nio.ch.FileChannelImpl では、メモリ確保できなかった場合のリトライの前に System.gc() が呼ばれたりします。


System.gc() を制限する JVMオプション

メモリ確保できなかった場合のリトライ時の FullGC は妥当ですが、RMIの分散ガーベッジコレクションで、非常に長い停止を伴う Stop The World の FullGC が定期的に発生するのはあまり許容できるものでもないでしょう。

これらの FullGC は、以下の JVM オプションで制御することができます。

  • -XX:+ExplicitGCInvokesConcurrent : System.gc() による FullGC の代わりに、並行GCサイクルを呼び出すようにする
  • -XX:+DisableExplicitGC : System.gc() の呼び出しを無効にする

なので、G1GC 利用時には、常に -XX:+ExplicitGCInvokesConcurrent を指定しておくのが良いでしょう。


-XX:+ExplicitGCInvokesConcurrent デフォルト化

JDK の Issues には、デフォルトで -XX:+ExplicitGCInvokesConcurrent を有効にすべきではないか? というものがポストされています。

Consider making XX:+ExplicitGCInvokesConcurrent the default for G1

検討すべきは、System.gc() 呼び出し時の「初期状態」で最も望ましい動作です。 シングルスレッドのStop The World GC(非常に長い停止)とすべきか、それとも G1 の並行サイクル開始とすべきか?

JDK 9 の頃に作成され、現在までオープンのまま動きがないですが。