ES/1 NEO CSシリーズを長年ご利用いただいているお客様から、WebSphereのJavaヒープ管理のためのグラフ設定をご依頼いただきました。管理対象のシステムでJavaのOut Of Memoryが発生してしまったために、対処したいということでした。
今回は、Out Of Memory事象の調査結果と、その後のJavaヒープ管理の事例をご紹介します。
ES/1 NEO CSシリーズのCS-Java for WebSphereオプションでは、時間毎のヒープメモリ使用量を取得しています。そこで、Out Of Memoryが発生した日のグラフを作成し、ヒープ使用状況を確認することにしました。
その結果、ヒープ使用量が増加しているにもかかわらず、ヒープサイズが減少していることがわかりました(図1)。特に20時頃にその傾向はピークを迎え、ヒープ使用量がヒープサイズに到達してしまっています。なお、ヒープサイズはヒープ最大設定値に到達しておらず、余裕がある状況です。
次に、OSメモリ使用量を確認しました。20時頃、OSメモリ使用量を示す「Memory Used」は増加していました(図2)。
ユーザー・コマンド毎のメモリ使用量の内訳を確認すると、「root(java)」が使用していることがわかりました(図3)。
なぜ、Javaにはヒープ上限値が設定されているにもかかわらず、ヒープ上限値を超えたメモリ使用量が報告されているのでしょうか。
特に今回の事象ではJavaヒープサイズが減少傾向にあったにも関わらず、Javaコマンドのメモリ使用量は増加傾向にあります。
理由は、Javaのメモリ管理の特徴にありました。
Javaが使用する領域は大きく分けて2種類あります。JVM管理領域とOS管理領域です(図4)。
JVM管理領域の場合、ヒープサイズの上限値を指定でき、その中でGCを発生させるなどして上限値を超えないようにJavaがメモリを管理します。一方、OS管理領域についてはOSが管理するネイティブメモリ上の領域に取られます。最近ではメタスペースについて上限が指定できるようになりましたが、基本的にOSが領域を獲得できれば上限はありません。
今回の事象では、Javaコマンドの中でもOS管理領域のメモリ使用量が増加し、OSの空きメモリ量が減ったことで、ヒープサイズを拡張することができなかったことがOut Of Memoryの原因と考えられます。
なお、OS管理領域のメモリ使用量の増加の原因については特定できませんでしたが、恐らく、入出力に使用されるCヒープ領域の増加が発生したと考えられます。理由は、該当時間帯、データベースアクセスを示すJDBC Pool毎のコネクション使用回数が増加していたためです(図5)。
今回の事象を受けて、Javaヒープを定常的に管理するため図6のようなグラフを提案しました。JVMヒープ使用量の情報に、OS全体のメモリ使用量や、Javaコマンドのメモリ使用量を加えたグラフです。
OS領域も同時に可視化することにより、ヒープ内の使用量だけでなく、OS管理領域のメタスペース、Cヒープ、スレッドスタックの増加についても気付きのきっかけになると考えられます。
また、詳細インターバルのグラフだけではなく、図7のような1日単位のサマリ情報を長期スパンでグラフ化することもご提案しました。
長期グラフの場合には、最小ヒープ使用量を確認することで、GCにより解放されたあとのヒープ使用量を確認できると考えられます。もし、解放後ヒープ使用量が増加傾向にある場合には、GCでも解放されていない領域が増加している事象、いわゆる「メモリリーク」の気付きにつながります。
JavaヒープのOut Of Memoryの予防には、Javaヒープ使用量だけでなく、OSメモリ使用量も確認する必要があります。
本記事が皆様のパフォーマンス管理の一助となれば幸いです。