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.Bits や sun.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 の頃に作成され、現在までオープンのまま動きがないですが。