どのようにして "java.lang.OutOfMemoryError:Java heap space"エラーに対処するには?

私は、クライアントサイドのSwingアプリケーション(グラフィカルなフォントデザイナー)をJava 5で書いています。最近、java.lang.OutOfMemoryError.Java heap spaceというエラーが発生しています。というエラーが発生するようになりました。ユーザーは無制限にファイルを開くことができ、プログラムは開いたオブジェクトをメモリ内に保持します。調べてみると、Ergonomics in the 5.0 Java Virtual Machineや、Windowsマシンでは、JVMのデフォルトの最大ヒープサイズは「64MB」だと言っている人もいました。

このような状況では、この制約にどのように対処すればよいのでしょうか?

javaのコマンドラインオプションを使って最大ヒープサイズを増やすこともできますが、それには利用可能なRAMを調べて、起動用のプログラムやスクリプトを書かなければなりません。また、ある無限の最大値まで増やしても最終的に問題が解決するわけではありません。

メモリを解放するために、オブジェクトを頻繁にファイルシステムに保存するようにコードを書き換えることもできます(データベースを使うのも同じことです)。うまくいくかもしれませんが、おそらく大変な作業になるでしょう。

もし、上記のアイデアの詳細や、自動仮想メモリ、動的にヒープサイズを拡張するなどの代替案を教えていただければ幸いです。

ソリューション

最終的には、どのようなプラットフォームで動作しているかに関わらず、使用できるヒープの最大値は常に有限です。Windowsの32ビット版では、これはおよそ「2GB」です(特にヒープではなく、プロセスごとのメモリの合計量です)。これはたまたまJavaがデフォルトを小さくしているだけです(おそらくプログラマーがこの問題に遭遇せずにメモリの割り当てを暴走させるようなプログラムを作れないようにして、何をしているのか正確に調べなければならないようにしているのでしょう)。

このように、必要なメモリ量を決定したり、使用しているメモリ量を減らしたりするには、いくつかの方法があります。JavaやC#などのガベージコレクション言語でよくある間違いは、もう使っていないオブジェクトへの参照を持ち続けたり、代わりに再利用できるのに多くのオブジェクトを割り当てたりすることです。オブジェクトへの参照がある限り、ガベージコレクタはそれらを削除しないため、それらはヒープ空間を使用し続けます。

この場合、Javaメモリプロファイラを使用して、プログラム内のどのメソッドが大量のオブジェクトを割り当てているかを確認し、オブジェクトが参照されなくなるようにする方法や、そもそもオブジェクトを割り当てないようにする方法があるかどうかを判断します。私が過去に使った方法の一つに、"JMP" があります。

もし、何らかの理由でこれらのオブジェクトを割り当てていて、参照を維持する必要があると判断した場合は(やっていることによってはそうなるかもしれませんが)、プログラムを起動するときに最大ヒープサイズを大きくすればよいでしょう。しかし、メモリプロファイリングを行い、オブジェクトがどのように割り当てられているかを理解すれば、どれくらいのメモリが必要なのか、より良いアイデアが得られるはずです。

一般的に、プログラムが有限のメモリ(おそらく入力サイズに依存する)で実行されることを保証できない場合、常にこの問題に直面します。ここまでやって初めて、オブジェクトをディスクにキャッシュすることなどを検討する必要があるでしょう。この時点で、「XGBのメモリが必要だ」と言うにはそれなりの理由が必要で、アルゴリズムやメモリの割り当てパターンを改善しても、それを回避することはできません。一般的には、データベースや科学的な分析プログラムのような大規模なデータセットを扱うアルゴリズムの場合にのみ、このような状況が発生し、キャッシングやメモリーマップドIOのような技術が有効になります。

解説 (1)

ヒープのサイズを最大*に設定するコマンドラインオプション「Xmx」を付けてJavaを実行します。

詳細はこちらを参照してください。

解説 (3)

はい、-Xmxを使用すると、JVMに対してより多くのメモリを設定することができます。 メモリをリークしたり無駄にしたりしていないか確認してください。ヒープダンプを取り、Eclipse Memory Analyzerを使ってメモリ消費量を分析してください。

解説 (1)