Java: JDK 6u21 以降 + Eclipse 3.x で "OutOfMemoryError: PermGen space" が発生する場合の対処方法

先日リリースされた JDK 6u21 上で Eclipse を使うと、

java.lang.OutOfMemoryError: PermGen space

というエラーが発生することがある。このメッセージはエラーダイアログに表示されたり、ワークスペース内のログファイルに出力されたりする。
というような話を先日の日記で書いて、「HotSpot のバージョンが上がったからかな?」なんて適当なこと言ってたら、実際は別の原因だったらしい。

対象環境

  • Oracle (旧 Sun) の JDK 6u21 (JDK 6 Update 21) 以降を使っていて、
  • その上で Eclipse 3.3/3.4/3.5/3.6 を使っている

解決方法 (回避方法)

前回も書いたけど、eclipse.ini に下記のような行を追加すればいい。-vmargs と書いてあるよりは後ろの行でないといけないことに注意。

 -XX:MaxPermSize=128m

これは、HotSpot JVM が使用するメモリのうち、PermGen 領域のサイズを明示的に指定するものだ。上ではとりあえず 128m (128MB) を指定したけど、環境によっては、これより少なくても OK だったり、逆に大きくしないとダメかもしれない。
なお、Eclipse 3.6 の次回以降のリリースでは、これをやらなくてもエラーが発生しないよう修正される見込み。

[2010-09-26 追記] JDK 6u21 b07 / Eclipse 3.6.1 について

JDK 6u21 の最新ビルド b07 を使うか、Eclipse の最新バージョン 3.6.1 (Helios SR1) を使うと、上述の解決方法 (回避方法) は不要になるようだ。詳細は id:muimy さんによる下記記事を参照。

人類みんなごくつぶし - Eclipse and Java 6u21-b07
http://d.hatena.ne.jp/muimy/20100923/1285225819

原因

InfoQ 英語版で、原因が説明されていた。

InfoQ - Eclipse and Java 6u21 problems (英文)
http://www.infoq.com/news/2010/07/eclipse-java-6u21

"-XX:MaxPermSize" は、-XX という接頭辞が示すとおり、JVM ベンダ固有のオプションだ。Sun の HotSpot JVM 上で Eclipse を動かす場合には "-XX:MaxPermSize" を使って PermGen 領域のサイズを大きめに確保する必要があるが、Sun 以外の JVM でこのオプションを指定するとエラーになる場合もある (HotSpot 以外の JVM には、そもそも PermGen 領域というもの自体があるとは限らないので)。
そこで Eclipse では、ランチャ (Windows で言えば eclipse.exe) から JVM を起動する際、JDK 内の DLL に書かれている社名を調べて、「これは Sun の HotSpot JVM っぽいから "-XX:MaxPermSize" を指定してもよさそうだな」「これは Sun のじゃなさそうだから "-XX:MaxPermSize" を指定するのはやめておこう」といった判断をしている。
ところが、JDK 6u21 では DLL に書かれている社名が "Sun Microsystems, Inc." から "Oracle Corporation" に変更された。すると、実体としては Sun の頃と同じ HotSpot JVM なのに、Eclipse 的には「"-XX:MaxPermSize" を指定するのはやめとこう」という判定になってしまう。その結果、必要なだけの PermGen 領域を確保できず、冒頭に書いたような "OutOfMemoryError: PermGen space" というエラーが発生することになる。
……以上が InfoQ 記事の要約。
eclipse.ini で -vmargs の後に書いた内容はランチャ (eclipse.exe) から JVM にそのまま素通しで渡されるので、手動で "-XX:MaxPermSize=128m" を追加してあげれば、上記のように JVM ベンダが誤判定されていても PermGen 領域をちゃんと確保できるようになる、ということか。

教訓

同じような仕組みを自分で作ってみる場合を想像すると、判定に使っているデータが将来変わることまで予測しつつ、特定の JVM 固有の機能まできちんとサポートするっていうのは、ちょっと無理がある。しかし、-vmargs のようにユーザが後から調整できる、いわばマニュアル運転の部分を残しておくことで、「対処方法はないので JDK をバージョンアップしないように」なんていう事態を避けることができる。
もちろん、なんでもかんでもマニュアル運転で対処しろというのでは不便になる。大半の開発者が Sun JDK を使っているのがわかっているのに、「Eclipse をインストールしたらまず最初に eclipse.ini へ -XX:MaxPermSize=128m を追加する」という作業が必須になってしまう。
どこまでを作り込みで支援して、そこから先の部分をどうやって救うのか、という切り分けが、使いやすくするためのポイントなのかもしれない。