-XX:+DisableExplicitGCに関するJDK7とJDK8の違い

この記事は JVM Advent Calendar 2014の12/5分の記事*1です。昨日は
@jyukutyoさんのJITWatchでJITコンパイルを見よう!でした。


HotSpotには-XX:+DisableExplicitGCというオプションがありますが、この挙動がJDK8のリリースから変わっていたのでまとめます。

-XX:+DisableExplicitGC とは

System.gc()GCリクエストされても無視するオプションです。アプリケーション中でSystem.gc()を実行されるとメジャーGCが発生します。HotSpotに任せた場合はマイナーGCで十分回収できたかもしれないので、アプリケーションでSystem.gc()を呼び出すのはあまり好ましいことではありません。

無視するといっても内部的にそんな難しいことは行われておらず、OpenJDK8のソースを見るとif文でオプションが有効だったらGCしないだけです。

jdk8u/hotspot/src/share/vm/prims/jvm.cpp

451 JVM_ENTRY_NO_ENV(void, JVM_GC(void))
452   JVMWrapper("JVM_GC");
453   if (!DisableExplicitGC) {
454     Universe::heap()->collect(GCCause::_java_lang_system_gc);
455   }
456 JVM_END

JDK8よりjcmd <pid> GC.runに対しても有効になる

JDK7までは -XX:+DisableExplicitGC が有効化されていても、jcmd GC.runでGCリクエストが受け付けられていたのですが、JDK8より以下のメッセージ『Explicit GC is disabled, no GC has been performed.』が返ってきてGCは動きません。

$ jcmd 18952 GC.run
18952:
Explicit GC is disabled, no GC has been performed.

ソースを見ると、JDK8よりDisableExplicitGCフラグのチェックがjcmd <pid> GC.runの実装に追加されています。

jdk8u/hotspot/src/share/vm/services/diagnosticCommand.cpp

258 void SystemGCDCmd::execute(DCmdSource source, TRAPS) {
259   if (!DisableExplicitGC) {
260     Universe::heap()->collect(GCCause::_java_lang_system_gc);
261   } else {
262     output()->print_cr("Explicit GC is disabled, no GC has been performed.");
263   }
264 }
この修正による影響を受けるシステム

例えば、以下のようなシステムで影響を受けると思います。

  • 意図的にcronなどで定期的にjcmd <pid> GC.runを実行しているシステム
  • 連続運転テストをしてもメジャーGCが発生しないので、テスト終了時にjcmd <pid> GC.runを実行してJavaヒープにリーク傾向がないか考察しているシステム
回避策

JDK8において-XX:+DisableExplicitGC有効時にコマンドラインGCリクエストしたい場合は、jcmd <pid> GC.class_histogramによるヒストグラム取得の副作用を利用してGCを発生させることができます。

この場合、GCログのGC Causeに以下のように表示されます。

[Full GC (Heap Inspection Initiated GC)  39999K->37578K(250368K), 0.5085624 secs]

まとめ

  • JDK8より起動オプション-XX:+DisableExplicitGC有効時にはjcmd <pid> GC.runは無視されます
  • jcmd <pid> GC.class_histogramによってメジャーGCのリクエストをすることは可能です

*1:ええ、申し込んだのは2015/1/7ですがせっかく空いてたので