OutOfMemoryError: GC overhead limit exceeded

発生頻度は低いのですが、ある特定の条件の下で以下のOutOfMemoryError(以後OOM)が発生します。
java.lang.OutOfMemoryError: GC overhead limit exceeded

通常のOOMは「GCを行ったが次の処理に必要なメモリが確保できなかった場合」に発生します。しかし、このOOMは「GCを行った結果、次の処理に必要なメモリは確保できたが、処理が長い割には少ししか回収できなかった場合」に発生します(GC処理が長いだけでも回収量が少ないだけでも発生しません)。※私の経験上このOOMはFullGCが頻発した後に発生します。

オプションに
-XX:-UseGCOverheadLimit
を追加すれば、このOOMを発生させないようにする事ができます。しかし、それでも普通のOOM「Java heap space」が発生したり、そうでなくともGC時間が長すぎて処理がなかなか進まないという結果になりかねません。

結局「Java heap space」のOOMと同じで
  • コマンドライン・オプションの「-Xmx」で、ヒープをより多く確保するよう指定する
  • マシンの物理メモリが足りなければ増設する
  • どうしても拡張できないのなら「-XX:NewRatio」や「-XX:SurvivorRatio」などで、ヒープ内部の領域を調節してうまくやりくりする(中級者以上向け)
等の対策を行う事が望ましいでしょう。


以下は公式ページ(Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning)の抜粋と日本語訳です。※私は英語が苦手で日本語訳が怪しいので、間違い・よりよい訳がありましたらご連絡ください。

Excessive GC Time and OutOfMemoryError(原文)

The concurrent collector will throw an OutOfMemoryError if too much time is being spent in garbage collection: if more than 98% of the total time is spent in garbage collection and less than 2% of the heap is recovered, an OutOfMemoryError will be thrown.

This feature is designed to prevent applications from running for an extended period of time while making little or no progress because the heap is too small. If necessary, this feature can be disabled by adding the option -XX:-UseGCOverheadLimit to the command line.

The policy is the same as that in the parallel collector, except that time spent performing concurrent collections is not counted toward the 98% time limit. In other words, only collections performed while the application is stopped count toward excessive GC time.

Such collections are typically due to a concurrent mode failure or an explicit collection request (e.g., a call to System.gc()).


過度のGC時間とOutOfMemoryError(独自訳)

あまりにも多くの時間がGCに費やされている場合、concurrentGCは、OutOfMemoryErrorをスローします(GCの合計時間が全体の98%以上に費やされる場合でかつ、ヒープの回収が2%未満だった場合)。※98%以上・2%未満の部分の元文はそれぞれ「more than 98%」「less than 2%」なので、もしかしたら98%超・2%未満が正しいかもしれない。しかし文脈から判断し98%以上・2%未満とした。

この機能は、ヒープが小さすぎるために発生するほとんど(あるいはまったく)進まない処理を、長時間にわたって実行することを防ぐために設計されています。この機能は(もし必要な場合は)、コマンドライン・オプションに「-XX:-UseGCOverheadLimit」を追加することによって無効にすることができます。

このポリシーは、parallelGCの方針とも同じですが、concurrentGCの実行に費やす時間は98%の制限時間に数えません。言い換えると「アプリケーションが『過度のGC時間』に近づいている状態は、GCだけしか実行していない状態である」といえます。※この行の訳は特にアヤシイ

このようなGCは一般的に、concurrentモードの失敗または明示的なGCの要求(「System.gc()」の呼び出しなど)に起因しています。

2012/10/20