1. TOP
  2. BLOG
  3. TECH ARTICLE:Out of Memoryを
    解決するアプローチ
TECH ARTICLE

2023年11月21日

Out of Memoryを
解決するアプローチ

JAVAのプログラミングでは、開発者がメモリ管理をする必要がありません。これはCやC++にはない画期的なことです。このメモリ管理の概念は開発者の大きな支持を得て、JAVAはシステム開発では主流の言語になりました。
しかし、JAVAでもメモリの問題は発生します。CやC++のように頻繁には発生しないものの、Out Of Memory Errorは発生します。このエラーは運用担当者と開発者が最も嫌う問題の一つです。

JAVAのメモリ不足の3つのタイプ

Out Of Memory Errorが発生する原因は概ね3つに分類されます。
単純にメモリが不足する場合、処理の重いサービスが実行された場合、メモリリーク発生の場合によるメモリ不足で、以下に各々説明します。

(1) 単純にメモリが不足

これは、設定されたメモリ量が要求されるメモリ量より少ないということです。
プログラムまたはその他のミスではなく、設定された値が小さいので、通常は -Xmxの値を大きくすることで解決されます。実際には、JVM開始時にメモリオプションを忘れる場合が意外と多いことも事実です。また、-Xmx256のようにメモリの単位(設定-Xmx256m)を忘れる場合もあります。当然、オプションを誤って設定することは単純なミスですが、JAVAヒープメモリについて、経験がないために単位時間当りのサービスリクエスト数(Service Arrival Rate)から算出される値より少ないサイズを設定してしまう場合もあります。

Oracle/HP JVMではperm領域が小さくて発生する場合があります。perm領域でクラスコードなどが保存されるため、使用するクラスが多い場合にはサイズを増やすべきです。

(2) 処理の重いサービスの実行によるメモリ不足

アプリケーションサービス(例:Webアプリケーションの処理)が瞬間的にメモリを過剰に使用する事で発生します。
例えば、データベースで多くのデータをロードする場合や、検索サーバで不適切な検索条件で非常に多くのテキストを照会する場合などがこれに該当します。また、過去にアップロードやダウンロードのプログラムでよく見かけるbyte[] bufferでバッファーサイズの設定が大き過ぎて連続空間の不足(IBM JVM)が発生する場合や、同時に複数のリクエストが発生し、単位時間当りの絶対使用量が多くてメモリ不足が発生する場合なども考えられます。後者の場合はアプリケーションが正常にメモリを多く使っているのか、単にプログラムミスなのか、判断が難しいです。

(3) メモリリーク発生によるメモリ不足

このメモリ不足は、正確には、JAVAでロジック的に使わないオブジェクトがGCできない強い参照(strong reference)によって残され、メモリ使用量が増加する状態です。
ほとんどの場合、使用したオブジェクトが適切に解放されないことで発生します。代表的なのは、JDBC関連クラスを使用した後close()しないで発生する場合が考えられます。

また、キャッシュのロジックの問題もあります。
キャッシュで不要なデータ削除のロジックが正常に動作しない場合、Out Of Memory Errorが発生することがあります。包括的な意味でオブジェクトプールに不要なオブジェクトが増加することやHttpセッションでデータが増加する現象もこれに含まれます。

Out Of Memory Error発生時の原因分析

Out Of Memory Errorが発生した時には、画面又はプログラムのログでその原因を正確に判断する必要があります。
単に設定が正しくないのであれば、通常はJVM 起動時又は起動後、すぐにOut Of Memoryが発生します。そのため、サーバを起動した時にOut of Memory Errorが発生した場合は、先ずオプションの設定を疑うべきです。
しかし、perm領域が不足する場合は、少し時間をおいてから発生します。特にOracle/HP JVMを使用中に、ヒープメモリが不足していないのにOut Of Memory Errorが発生する場合は、先にperm領域が不足していないかを確認すべきです。

もし上記 (2) の処理の重いアプリケーションが実行され、Out Of Memoryが発生した場合は、ヒープメモリのグラフで、一般的に突然メモリ使用量の急激な増加現象を観察できます。
時間によってはメモリ使用量が増加せず、ある瞬間だけメモリ使用量が増加しGCが頻繁に発生した場合、特定のアプリケーションがメモリを過剰に使っていると推測できます。もちろん、アクティブサービスが増加するとメモリ使用量が増加しGCが頻繁に発生しますが、メモリが不足してアクティブサービスが増加したのか、アクティブサービスが増加してOut Of Memoryが発生したのかは不明です。しかし、これまでの経験ではアクティブサービスが増加してOut Of Memoryが発生した事例はほとんどありません。

IBM JVMの場合、ヒープに十分余裕があり、GCが頻繁ではないのに突然Out Of Memory Errorが発生する場合があります。これは、メモリの断片化による連続空間不足を疑う必要があります。

最後に、メモリリークが発生しメモリ不足が発生すると、ヒープメモリ使用量のグラフが時間帯によって増加する現象が現れます。
長時間のメモリ使用量の変化を観察するとメモリリークがあるかどうかを確認できます。サーバ起動後、Out Of Memoryの発生までの時間は、数時間の場合もあれば、1ヶ月以上の場合もあります。

JENNIFERでOut Of Memory Errorを解決

単に設定が正しくなく、メモリ設定が少な過ぎる場合には設定を修正すれば問題は解決できます。しかし、特定アプリケーションによるOut Of Memory Errorやメモリリークが発生する場合には、その解決には論理的なアプローチが必要です。

特定アプリケーションによって不特定な時間にOut Of Memory Errorが発生することが確認された場合、どのアプリケーションがエラーを発生させているかを調査する必要があります。
もし該当アプリケーションがアップロードやダウンロードと関連あるプログラムであれば、データを制御するためのバッファーサイズを確認し、単純なDBをアクセスするプログラムであれば、DBアクセスのデータ量を確認すべきです。
JENNIFERはサービス実行中にOut Of Memory Errorが発生するとエラーを知らせて、エラー情報を保存する機能があります。

メモリを過剰に使うサービスが実行されると瞬間的に該当インスタンスの応答時間とCPU使用量などが増加して、X-View上で全てのプログラムの応答時間が急激に増加する現象が観察できます。この時、応答時間が長く、更にCPU使用量が多いものが問題のアプリケーションである場合が多いです。従って、日付別のX-Viewデータ照会によって問題の時間帯を綿密にモニタリングすると、簡単にこれを見つけることができます。

メモリリークを解決するにはJAVAの高度なチューニングスキルが必要で、難しいです。
開発サーバではなかなかその現象を再現できないため、分析までに長い時間が必要です。メモリリークを検知するには、時間の経過によってメモリが増加していることを確認することが重要です。短い時間のメモリ使用量ではなく一日全体のヒープメモリ使用量の変化を確認できる画面が必要です。

ヒープダンプによってメモリリークの原因を探すこともできますが、それもヒープダンプを分析するスキルがないと難しいです。
特定アプリケーションを何回も実行してメモリ変化を確認する方法は開発サーバでは可能ですが、運用環境では不可能です。また、-Xrunhprofオプションを利用したヒープ分析は現実的に運用環境では難しいです。しかし、APMによってメモリリークを解決する機能は運用環境で使用できるようにしておくべきです。

JENNIFERには、メモリリークを追跡する機能がありますが、JDBCリソースの未解放をほぼ完全に(一部特殊な状況は除く)検出します。メモリリークを検出した場合、JDBC リソースの未解放コードの修正を推奨します。

また、コレクションオブジェクトのサイズのモニタリング機能があります。
これはJENNIFERだけが提供できる特別な機能で、数多くのメモリリークを分析した結果、メモリリークはほとんどコレクション関連のオブジェクトで発生することが分かりました。キャッシュ/プールコンポーネントは通常内部にコレクションタイプのオブジェクトにデータを保存しますが、これが原因でメモリリークを発生させる場合が多いです。
逆に言えば、あるコレクションオブジェクトのサイズが増加することをモニタリングできれば、多くのメモリリークが検知できます。

お気軽にお問い合わせください

製品に関する事やご不明な点など、お気軽にご相談ください。
また、ジェニファーソフトでは2週間の無償トライアルを提供しています。

詳しく知りたい方は
導入や費用のご相談は