JAVAでは、開発者がプログラミングをする時、別途メモリ管理をする必要がありません。これはC、C++ではない画期的なことです。JAVAのメモリ管理概念は開発者を熱狂させ、これによりJAVAはシステムを支配する言語になりました。しかし、 JAVAでもメモリ関連問題は発生します。C、C++のように頻繁には発生しないものの、時々OutOfMemoryErrorは発生します。このエラーは運用担当者及び開発者が一番嫌がる問題の一つです。
JAVAのメモリ不足の3タイプ
OutOfMemory Errorが発生する原因は大きく見ると3つに区分されます。すなわち、 ただ(1)メモリが不足する場合 (2)Heavy サービスが実行された場合 (3) メモリ リークによる メモリ 不足の3つです。
(1)のただメモリが 不足するというのは、設定されたメモリ量が要求されるメモリより少ないということです。プログラム又は その他ミスではなく、ただ 値が少なめに 設定されたのであり、通常は -Xmx値を大きくすることで解決されます。実際には、JVM 開始時、メモリオプションを忘れる場合が意外と多いのも事実です。又は -Xmx256のように メモリ 単位(設定-Xmx256m)を忘れる場合もあります。もちろん、オプションを誤って設定するのは単純なミスですが、JAVA ヒープメモリについての経験がないために単位時間当りサービスリクエスト数(サービス Arrival Rate)より少ないメモリを設定してしまう場合もあります。
SUN/HP JVMでは perm 領域が小さくて発生する場合があります。メモリのperm領域でクラス コードなどが保存される領域に使うクラスが多い場合にはサイズを増やすべきです。
(2)二つ目にOutOfMemoryErrorは応用サービス(ex Web Request 処理ロジック)が 瞬間的にメモリを過度に使用する事でも発生します。
例えばデータベースで多くのデータのローディングをする場合や、検索サーバーで検索条件が適切でなく多くのテキストを照会する場合などがこれにあたります。また、過去にupload/downloadプログラムでよく発見されたbyte[] bufferでbufferサイズの設定が大きすぎて連続空間不足現象(IBM JVM)が 発生する場合や、同時にマルチにリクエストが発生し 単位 時間当りの絶対使用量が 多くメモリ不足が発生する場合なども考えられます。 もちろん、後者の場合は アプリケーションが正常にメモリを多く使うのか、単にプログラムミスなのか、判断は困難です。
(3)最後に メモリリークが発生してメモリが不足する場合です。正確には、JAVAで メモリリークは ロジック的に使わないオブジェクトが GC出来ないstrong referenceにより 参照されメモリ 使用量が 増加する状態をいいます。ほとんどの場合、適切な解放を行わないことで発生します。代表的なのは、JDBC関連 クラスを 使う close()を リクエストできなくて発生する場合が考えられます。(勿論、 JDBCによって又は Web アプリケーション Serverによって メモリ リークが 発生しない場合もあります。)
また、Cacheのロジックに題がある場合もあります。 CACHEで不要な データの削除ロジックが正常に動作しない場合 OutOfMemoryErrorが発生することがあります。包括的な意味でConnection Poolで不要な Connectionが増加するか、 Http Sessionでデータが増加する現象もこの部分に含まれます。
OutOfMemoryError 発生時の原因分析
画面又はプログラムログでOutOfMemoryErrorが発生した時、その原因を正確に判断する必要があります。ただ 設定が 正しくないのであれば通常はJVM startup 又は startup後、すぐにOutOfMemoryが 発生します。
そのため、サーバをスタートした時、OutOfMemoryErrorが発生した場合は まずオプション設定を疑うべきです。 しかし、PERM領域が 不足する場合には、少し時間をおいてから発生します。ヒープメモリが不足しないのにOutOfMemoryErrorが発生してSUN/HP JVMであれば PERM領域が 不足していないかを先に確認すべきです。
もし (2)Heavy Load アプリケーションが実行されOutOfMemoryが 発生した場合は どのような現象が起こるのでしょうか。 ヒープ メモリ グラフを見ると、突然メモリ使用量が急激に増加する現象が見られるのが一般的です。 時間によってはメモリ使用量が増加せずある瞬間だけにメモリ使用量が増加しGCが頻繁になる現象が発生した場合、特定アプリケーションがメモリを過度に使っていることが推測されます。もちろん、 Active サービスの増加後、メモリ使用量が増加しGCが頻繁になって(メモリが 不足してActive サービスが 増加したのか、Activeサービスが増加してOutOfMemoryが 発生したのか)は不明確です。ただし、これまでActive サービスが増加してOutOfMemoryが発生した例はほとんどありません。
IBM JVMの場合、ヒープに十分余裕があり、GCが 頻繁ではないのに突然 OutOfMemoryErrorが発生する場合があります。この時は fragmentationによる連続空間不足を疑う必要があります。
最後に、メモリリークが発生しメモリ不足が発生すると、ヒープ Usageグラフが 時間によって増加する現象が現れます。長時間のメモリ使用量の変化を観察するとメモリリークがあるのかどうか確認できます。一般的に サーバ起動後、OutOfMemoryの発生までの時間は、数時間の場合もあれば、一ヶ月以上の場合もあります。
ジェニファーで OutOfMemoryErrorを解決
ただ単に設定が正しくなく、メモリ設定が少なすぎる場合には設定を修正する事で問題は解決できます。しかし、特定アプリケーションによるOutOfMemoryError、メモリリークが発生する場合には、解決のために論理的なアプローチが必要です。
特定アプリケーションによって不特定時点にOutOfMemoryErrorが発生することが確認されたら、どのアプリケーションがエラーを発生させるのか探す必要があります。 もし該当アプリケーションがupload、downloadと関連あるプログラムであればデータを制御するためのバッファー サイズを確認、また、単純な DBを使うプログラムの場合はDB Access データ量を確認すべきです。ジェニファーはサービス 実行中 OutOfMemoryErrorが 発生するとERRORを発行してエラー 情報を保存する機能があります。
通常は メモリを 過度に使うサービスが実行されると瞬間的に該当インスタンスの 応答時間/CPU使用量などが増加してX-VIEWの全てのプログラムの応答時間が急激に増加する現象が見られます。この時、上位部(応答時間が長い/CPU使用量が多い)に 位置するのが問題のアプリケーションである場合が多いです。したがって、日付別 XView データ 照会によって該当時間帯を綿密にモニタリングすると簡単にこれを見つけることができます。
メモリ リークはJAVA チューニングの能力がかなり必要で、解決しにくい問題です。
長い時間が必要で、開発 サーバではなかなか再現できないためです。メモリリークを確信する為には、 まず、時間によってメモリが 増加することを検知するのが重要です。
したがって、短い時間のメモリ 使用量ではなく一日全体のヒープメモリ使用量変化を確認する画面が必要です。(ジェニファー マニュアルをご参照ください。)
IBM JVMを使うシステムの場合、ヒープ DUMPによって簡単に原因を探すことができますがSUN/HPではかなり難しい 問題です。特定 アプリケーションを何回もリクエストしてメモリ 変化を確認する方法は開発サーバでは可能ですが運用環境では不可能です。また、-Xrunhprof:ヒープ オプションを利用したヒープ分析または現実的に運用環境での使用は難しいです。APMで全ての メモリ リークを解決するための機能は運用環境(production)で使用可能であるべきです。
ジェニファーには、 メモリリークを 追跡する機能があります。
まず、ジェニファーはJDBC リソース未解放をほぼ完璧(一部特集な状況は例外)に検出します。 メモリ リークが 発生するとまずJDBC リソース 未解放 コードの 修正を推奨します。
そしてジェニファーは Collection/Mapクラスサイズモニタリング機能を提供します。これはジェニファーだけが提供できる特別な機能です。数多くのメモリリークを IBM JVM /ヒープ DUMPで分析した結果、メモリリークはほとんどCollection/Map系列のクラスで発生することが分かりました。Cache/Pool コンポーネントは通常内部に Collection/Mapタイプの クラスにデータを保存しますが、これがエラーになってメモリ リークを発生させる場合が多いのです。逆に言えば、あるCollection/Mapの elements countが増加するのかをモニタリングできれば、多くのメモリリークが検知可能です。実際にジェニファーはこれにより多くのサイトで他社製品では解決できなかったメモリリークを解決してきました。
また、ジェニファー 3.2には新しい機能のCollection モニタリング 機能が追加されました。指定した jarファイルにおいて全ての static 変数に宣言された Collection/Mapを検索し、これを構成ファイルに設定することで element countを 追跡する機能です。(ジェニファーユーザマニュアルをご参照下さい。)
ジェニファーは実践的なソリューションです。 実際の運用環境で長年問題解決を行ってきた経験を基盤に、活用可能な機能を提供します。 |