本連載は、日立製作所が提供するアプリケーションサーバ「Cosminexus」の開発担当者へのインタビューを通じて、Webシステムにおける、さまざまな問題/トラブルの解決に効くノウハウや注意点を紹介していく。});今回は、Java開発者であれば、誰もが遭遇するメモリ不足エラー「OutOfMemoryError」に対する基本的な切り分け方法と、OutOfMemoryError発生につながる危険なケースを具体例を挙げて解説する。Javaのメモリは大きく分けて2種類Javaのメモリは大きく分けて「Javaヒープ」「Cヒープ」の2種類がある。Javaヒープは、Javaオブジェクトなどのプログラムのリソースを格納する領域、Cヒープは、JavaVM自身のリソースを格納する領域である。それぞれの領域が不足した場合、メモリ不足エラー「OutOfMemoryError」が発生する。Javaのメモリの内訳メモリ不足の問題の切り分け方の基本まずは、メモリ不足がJavaヒープとCヒープのどちらで発生したかを切り分けておこう。JavaヒープOutOfMemoryErrorがスローされ、JavaVMの実行が継続している場合には、Javaヒープが不足している可能性が高い。Javaヒープ不足かどうかを確定させるために、スローされたOutOfMemoryErrorのトレースを確認しよう。space」とともに出力され(【*1】)、OutOfMemoryErrorの発生位置がjavaソース【*2】であれば、Javaヒープ不足で確定である。Cosminexusの場合は、デフォルトでコンソールに以下のように出力されるという。VMがクラッシュしている場合には、Cヒープ不足で確定だ。この場合、コンソールには以下のように出力される。<message>」とともに出力されるのが特徴である(【*3】)。Methodまれに、Cヒープ不足でも、OutOfMemoryErrorがスローされる場合がある。この場合は、OutOfMemoryErrorのトレースが、以下のようになる。Method(【*4】)となっている。APIのnative実装部分でメモリ確保に失敗しているためである。「OutOfMemoryError」発生につながる危険なケースを以下に、いくつか紹介する。今回は、特に調査の難しいCヒープ不足に絞って解説する。これらの事例を参考にして、「OutOfMemoryError」発生を防ごう。その1:スレッド爆発!スレッドはスタック領域としてCヒープを消費する。スレッド当たりのスタック領域サイズは、以下の通りである。APIから暗黙的に作成する場合もある。このため、想定外にスレッドが作成されていないか、テスト段階で確認し、Cヒープ上限値を超えることがないかどうか確かめておくのがよいだろう。スレッド数の確認は、スレッドダンプを採取したり、JDKのjconsoleなどの監視ツールを使ったりすることで可能だ。その2:ファイル情報で使用する領域java.io.FileクラスのdeleteOnExitメソッドはアプリケーション終了時にファイルを削除してくれる便利なメソッドだ。メソッドで削除するファイルの情報をずっと保持するため、使えば使うほどメモリを消費する。5までは、この情報の保持先はCヒープとなっている。使いすぎに注意しないと、Cヒープを圧迫することになってしまう。6からは情報の保持先がJavaヒープになっている。6からとで消費するヒープが異なる点も要注意だ。JavaVMのバージョンアップなどのときに、メモリ使用サイズを確認しておくのがよいだろう。JDKバージョンスレッド当たりのスタック領域サイズ〜5.0Cヒープ6.0〜Javaヒープその3:大容量のI/Oファイル入出力や通信関連のAPIは、最終的にはネイティブなシステムコールで実現されている。そのため、入出力対象データは、いったんCヒープにコピーされた後、システムコールに渡される。コピー先のバッファは、8Kbytes未満の場合は当該スレッドのスタック上に取られることになるが、それを超える場合は、Cヒープに一時的に確保される。I/Oが完了すれば、その領域は解放される。あまりに大きなI/Oを行うと、一時的にCヒープを圧迫することになるので、気を付けよう。バッファサイズバッファの確保先8Kbytes未満スタック8Kbytes以上Cヒープその4:圧縮ライブラリで使用する領域java.util.zipパッケージのDeflaterクラスやInflaterクラスでは、コンストラクタの処理でCヒープ領域を確保する。確保されたCヒープ領域は、オブジェクトがGCされてfinalize()が実行されるか、end()メソッドを明示的に実行するまでは解放されない。そのため、これらのクラスのオブジェクトを大量に作成して保持していると、Cヒープを圧迫することになるので、気を付けよう。その5:バッファオブジェクトで使用する領域java.nio.ByteBufferクラスのallocateDirectメソッドや、java.nio.channels.FileChannelクラスのmapメソッドによりBufferオブジェクトを生成する場合、引数で指定したサイズの領域をmmapシステムコールにより確保する。そのため、大きなサイズを指定したり、大量のBufferインスタンスを生成した場合にメモリが圧迫される可能性があります。この領域はBufferオブジェクトがガベージコレクトされるまで解放されないので、気を付けよう。EEサーバからレスポンス返らず。アプリ開発でも、よ?く考えよう。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。