2023年10月13日
ヒープメモリリークと解決策
Javaの場合、開発者がプログラム内でメモリを自分で管理することはありません。
これはC/C++にはない画期的なことでした。Javaのメモリ管理の概念は、開発者の負担を軽くしてJavaを世の中に普及させる革新的なものでした。
しかし、残念なことにJavaでもメモリの問題は発生します。
C/C++のように頻繁ではないですが、時々Out Of Memory Error(以下、メモリ不足と表記)が発生していて、このエラーは運用者や開発者が最も困る問題の1つに違いありません。
1 .Javaのメモリ不足タイプ
メモリ不足が発生する原因は概ね、単純なメモリ不足と、重いサービスの実行によるメモリ使用量の急激な増加、メモリリークによるメモリ不足がありますが、以下に説明します。
(1) 単純なメモリ不足は、設定されたメモリサイズが必要なメモリサイズに比べて足りないことです。
プログラムやその他の不具合ではなく、単に設定されたサイズが小さいことなので、通常-Xmxのサイズを増やすことで解決できます。
(2) 重いサービスの実行は、アプリケーションが瞬間的にメモリを多く使用すると発生します。
データベースから余裕メモリ以上のデータを照会する場合、byte[]バッファーのサイズの設定が大き過ぎて連続したメモリ領域の確保ができない場合、一瞬、負荷が高くなり絶対的なメモリ使用量が多くてメモリ不足が発生することがあります。
(3) メモリリークは、使用されないオブジェクトをGCされないStrong Referenceが参照してメモリが増加する現象です。
ほとんどの場合、オブジェクトの使用後に適切に返却をしない時に発生します。よくあるケースとしてJDBC関連クラスの使用後にclose()をしないことがあります。
他では、cacheのロジックに問題がある場合で、 不要なデータの消去のロジックが正常に動作しないでメモリ不足が発生することがあります。
2. メモリ不足発生時の原因分析
メモリ不足が発生した場合、その発生理由を正確に判断する必要があります。
単純な設定ミスであれば通常JVMの起動中や起動直後の短い時間内にメモリ不足が発生します。
従って、APサーバの起動時にメモリ不足が発生したら、先ずヒープメモリ関連の設定を確認してみます。
もし、原因が上記(2)の場合であればヒープメモリの使用量のチャートが急激に増加しながらGCが頻繁に発生することが一般的です。
このような事象が発生する場合は、特定のアプリケーションでメモリ使用量が多過ぎる疑いがあります。
メモリリークでは、ヒープメモリのグラフが時間の経過と供にどんどん増加する傾向が表れます。
その傾向は速ければ数時間で表れることがありますが、数十日以上にかけて表れることもあります。そのため、メモリ使用量の推移を長時間モニタリングしないと把握できない場合が多いです。
3 .JENNIFERとメモリ不足
設定のミスやメモリ使用量の見積もりを間違えた場合は設定値の修正で問題を解決することが可能ですが、特定アプリケーションによるメモリ不足の発生やメモリリークが発生する場合は、問題解決のためにより論理的なアプローチが必要です。
特定アプリケーションによるメモリ不足の発生を確認した場合、エラーが発生するアプリケーションを探し、メモリ増加の原因を追究しなければなりません。
もし、アプリケーションでアップロードやダウンロードを行なっている場合は、データをコントロールするためのバッファーサイズを確認します。
DBを使用するアプリケーションの場合は、DBのデータ量を確認する必要があります。
JENNIFERはメモリ不足が発生するとイベントを発生するので、この内容を確認することも1つの方法です。
通常、メモリを使い過ぎるアプリケーションの場合、瞬間的に該当インスタンスの応答時間の遅延とCPU使用量が増加し、X-Viewで全てのトランザクションの応答時間が急激に遅延する現象が発生します。
この時、応答時間やCPU時間が最も大きいトランザクションが問題のアプリケーションである可能性が高いです。
メモリリークはある程度の経験がないと把握が難しいです。
その現象は長い時間をかけて表れ、開発環境では再現できないケースも多いです。
JENNIFERはメモリリークの把握を支援する機能を提供しています。
先ず、時間経過によってメモリ使用量の増加を検知することが重要です。
この現象を把握するため、1日以上のヒープメモリ使用量の推移を確認できる機能をJENNIFERは提供しています。また、メモリリークの主要原因であるCollection/Mapのサイズ(element数)モニタリング機能[メモリ(コレクション)]も提供しています。
Cache/Poolコンポーネントは通常ではCollection/Mapタイプのクラスにデータを保持します。この管理が正常にできない時にメモリリークがよく発生します。
従って、Collection/Mapのelement数をモニタリングできれば多くのメモリリークを把握することができます。