メモリは足りているのに“OutOfMemory”:事例に学ぶWebシステム開発のワンポイント(9)
本連載では、現場でのエンジニアの経験から得られた、アプリケーション・サーバをベースとしたWebシステム開発における注意点やヒントについて解説する。巷のドキュメントではなかなか得られない貴重なノウハウが散りばめられている。読者の問題解決や今後システムを開発する際の参考として大いに活用していただきたい。(編集局)
今回のワンポイント
Javaアプリケーションを動作させていて、「OutOfMemoryが出た」「Java VMが落ちた」という問い合わせを受けることがある。この場合、たいていはアプリケーションの問題や、設定の問題であることが多い。本稿では、HP-UX上でのJava VMを例に、OutOfMemoryが出る原因とその対処方法を紹介する。なお、本稿に登場する用語は第6回「APサーバからの応答がなくなった−GCをチューニングしよう−」で解説しているので、参照してから読んでほしい。
Old領域が足りない
WebLogic Serverなどのアプリケーションサーバを動作させる場合はメモリがかなり必要になってくるので、New領域やOld領域を必要なだけ大きく取る必要がある。しかし、いくらOld領域を多く取ってもOutOfMemoryが発生するという問い合わせを受けることもある。
ガベージ・コレクション(以下GC)を行う時点においてルートセットからたどれるオブジェクトが多過ぎるため、フルGCを行ってもOld領域に全部入りきらない場合にOutOfMemoryが発生する。不要になったオブジェクトへの参照を外し忘れてしまうことにより発生することが多い。いわゆるメモリリークであったり、メモリリークに見えるような状態であったりする。
メモリリークについては、OptimizeitやJProbeのようなメモリデバッガを使用して、解放されていないオブジェクトを発見し、解放するように修正する必要がある。例えば、サーブレットコンテキストに格納したデータはセッションタイムアウト後も削除されないデータなので、削除し忘れに注意する必要がある。
メモリリークに見えるような状態は、例えば、セッションタイムアウトが非常に長い場合である。ユーザーとのインタラクションとしては終了していても、セッションの無効化(invalidate)を行わないと、セッションタイムアウトまでセッションにデータが残ったままとなる。セッションタイムアウトになれば解放されるが、セッションタイムアウトが非常に長い場合は、すでに使用しないにもかかわらずGCでも解放されないオブジェクトとなるので注意が必要である。
HP-UXでは-Xverbosegcオプションによって詳細にメモリ使用状況を把握することができるので、常にメモリの使用状況をチェックしておく必要がある。
Java VMのPermanent領域が足りない
クラスやメソッドの情報などが入るPermanent領域が不足した場合にOutOfMemoryが発生する。WebLogic Serverなどのアプリケーションサーバを動作させている場合は、Permanent領域を大きくする必要がある(-XX:PermSize=<初期値> -XX:MaxPermSize=<最大値>)。HP-UXのJava VMでは-XverbosegcオプションでPermanent領域をチェックする必要がある。
カーネルパラメータ(maxdsiz)が小さすぎる
Java VMやアプリケーションサーバがnative method経由でメモリを確保(mallocなど)することがある。そこで確保したメモリの大きさがmaxdsizを超えた場合にOutOfMemoryが発生する。WebLogic Serverを動作させている場合は300Mbytes程度になることもある。samなどを使って、maxdsizを大きくする必要がある。かなり大きなサイズを指定してもこの状況に陥る場合は、native method内でのメモリリークも考えられる。
Glans Plusなどで該当のJava VMのメモリ使用状況を確認する。プロセスのデータ領域のVSSがmaxdsizを超えないようにする必要がある。
カーネルパラメータ(maxfiles、maxfiles_lim、nfile)が足りない
アプリケーションサーバなどでは、ソケットなども含めオープンするファイル数が多くなりやすい。そのため、ファイルに関するカーネルパラメータを調整する必要がある。samなどを使って、maxfiles、maxfiles_lim、nfilesの値を大きくする必要がある。
運用中のアプリケーションサーバについても、Glance Plusなどを使うことによって、現在オープンしているファイル数を取得することができるので、定期的にチェックしておく必要がある。
ほかのプロセスが起動できない
HP-UXのJava VMはEXEC_MAGIC形式を採用しているため、通常のSHARE_MAGIC形式のプログラムと比較して、プロセス固有のアドレス空間が広くなっている。また、UNIXにおいてほかのプロセスの起動はfork->execという手順を踏むために、execが行われる前のforkが同時に複数発生した場合、OSが必要とするメモリ量が現在確保しているメモリ量(物理メモリ+スワップ領域)を超えてしまう場合にOutOfMemoryが発生する。
アプリケーションサーバの場合はJSPの動的コンパイルなどに注意する必要がある。アプリケーションサーバには大量のメモリを割り当てていることが多いため、複数ページの動的コンパイルが同時に発生すると、アプリケーションサーバ自体のforkが発生し、一時的に大量のメモリを必要とすることになる。もちろん、JSPの動的コンパイルというのは一例なので、特にアプリケーションサーバなどでは、ほかのプロセスを起動させないように設定するということが必要である。
とはいっても、Javaではforkのみということはないし、たいていの場合はexec後のプロセスが必要としているメモリ量は小さい。ざっくりとした目安としてはforkが同時に2つ動作することぐらいまで想定していれば十分かと思われる。Javaのプロセスをforkするときに必要となるメモリ量も、該当プロセス全体のVSSとしておけば問題ないであろう。
なお、HP-UXの仮想アドレス空間などについては『HP-UX TUNING AND PERFORMANCE:Concepts,Tools and Methods』(ISBN 0-13-102716-6)が参考になる。
EXEC_MAGICで使えるPrivate領域を使い果たした
EXEC_MAGIC形式のプログラムは、プロセス固有の領域として2Gbytesまで使用することができる。プロセス固有の領域には、テキスト領域、データ領域、(プロセス固有の)メモリマップファイルなどが割り当てられる。Java VMのNew領域、Old領域、Permanent領域は(プロセス固有の)メモリマップファイル上に取られるということもあり、Large Heap機能を使用しないJava VMでは、約1.7Gbytesまでしかメモリを割り当てることができない。しかし、しかし、「カーネルパラメータ(maxdsiz)が小さすぎる」の節でも述べたとおり、アプリケーションサーバではデータ領域にもかなりのメモリが必要とされる。これらが原因で、プロセス固有の領域の2Gbytesを使い切ることがあり、そのときにもOutOfMemoryが発生する。特に、Java VMの起動オプションで1.5Gbytes程度のメモリを割り当てているところは注意が必要となる。
さて、最近のJava VMにはLarge Heapオプションが追加された。Large Heapとは、上で述べた1.7Gbytesよりも多くのメモリをJava VMに割り当てる設定である。簡単にいうと、EXEC_MAGIC形式でプロセス共有の領域として管理されているアドレス空間もプロセス固有の領域として使うことで、上限を引き上げるというものである。
HP-UX 11とHP-UX 11iでは対応が異なる
HP-UX 11iではJDK 1.3.1_01以降でLarge Heapオプションが使用できる。HP-UX 11ではJDK 1.3.1_06以降から使用することができる。使用できるメモリ量の詳細については、http://www.hp.com/products1/unix/java/java2/sdkrte1_3/infolibrary/sdk_rnotes_1-3-1-06.html#heapを参照してほしい。
ただし、根本的な対策としては、必要なメモリ量が少なくなるようにすることである。確かにLarge Heapを使用することでいくらか領域を拡大することはできる。しかし、領域を拡大するとGCの時間が長くなったり、「ほかのプロセスが起動できない」の節に示したようなforkを行う際に必要なメモリ量が増えたりなどと弊害が出てくることとなる。Large Heapオプションは取り急ぎの対応策とし、その間にアプリケーションを見直して使用メモリ量の削減を検討すべきだと思われる。
著者プロフィール
重畠 洋二(しげはた ようじ)
現在、株式会社NTTデータビジネス開発事業本部に所属。 技術支援グループとして、J2EEをベースにしたWebシステム開発プロジェクトを対象に、技術サポートを行っている。特に、性能・信頼性といった方式技術を中心に活動中。
- ファイルアップロード/ダウンロードに潜むわな− 大容量、高負荷時の注意点 −
- ブラウザキャッシュでパフォーマンス向上―負荷分散装置の落とし穴に注意−
- JDBC接続を高速化する− PreparedStatementキャッシュの威力−
- レスポンスキャッシュでパフォーマンス向上―アプリケーションを変更せず性能UPする魔法のつえ―
- メモリは足りているのに“OutOfMemory”
- 文字化け“???”の法則とその防止策
- 低負荷なのにCPU使用率が100%?
- APサーバからの応答がなくなった、なぜ?―GCをチューニングしよう―
- サービス中にアプリケーションを入れ替える―クラスタ機能を活用しよう―
- マルチスレッドのいたずらに注意―あなたのJSPやServletは大丈夫?―
- クラスタは何台までOK?―性能から見たWebLogicクラスタの適正台数―
- キャッシュが性能劣化をもたらす“なぞ”を解く― データのキャッシュとコネクションプール ―
- クラスタ化すると遅くなる?― HttpSessionへの積み過ぎに注意 ―
Copyright © ITmedia, Inc. All Rights Reserved.