さて、これまでの経緯を簡単にまとめてみよう。
ここまで事象が特定されていれば、伝家の宝刀PrintClassHistogramオプションにご登場願おう。ここで簡単におさらいすると、PrintClassHistogramは、Javaヒープ内に存在するインスタンスの個数と総サイズを、クラスごとの統計情報として出力するオプションだ。
クラスごとに、この統計情報を時系列に追うことで、どのクラスがメモリリークしている原因かを容易に特定できる。その上オーバヘッドも少なく、環境への変更も最低限で済むため、サービス中の商用環境でも利用できる使い勝手のよいJavaVMのデバッグオプションだ。詳しくは、前回を参照いただきたい。
PrintClassHistogramオプションを紹介すると、現場からは安堵(あんど)の声が漏れた。そして、早速PrintClassHitogramオプションを利用して、試験環境にて再現試験を行うことになった。
そして、年越し3日間の連続稼働の後、驚くべき事実を目の当たりにすることになる。
年明けのある朝、現場に到着すると、プロジェクトメンバがGCグラフを手に駆け付けてきた。
再現試験のGCグラフは、何の問題もないGCグラフだった。再現失敗か、何か見落としがあったのか。
「あ!!」
そのとき、プロジェクトメンバの1人が大きな声で叫んだ。指さしていたのは、同じくGCグラフだったが、そのグラフでは商用環境に近い動作をしていたのだ!!
確認を行うと、このGCグラフは、メモリリーク検証用のPrintClassHistogramオプションを意図的に設定しなかったJava常駐プロセスだという。
きな臭いにおいが漂い始めている。
さらにこのとき、この再現試験の裏で行っていたJavaプログラムのソースコードレビューを担当していた同僚が、面白い事実に気が付いた。それは、既存プログラムはすべて、何らかの形でRMI(Remote Method Invocation)を利用していたが、新規に追加したJavaプログラムでは一切RMIを利用していなかったのだ。
編集部注:RMIについて詳しく知りたい読者は、「Webアプリケーション実現の本質を知ろう」の「分散オブジェクト RMI & CORBA」をご参照ください。
すぐさま、RMIを利用しているJava常駐プロセスのGCグラフを確認すると、以下のようなものだった。なお、背景が黒くなっているが、誤植でも何でもない。RMIを利用すると、1分に1回Full GCが発生するため、黒い線が重なってこのように見えてしまうのだ。
これまでの3つのGCグラフを見比べると、メモリリークを引き起こしているGCグラフだけ、明らかな違いが見て取れるだろう。
これまで登場してきた4つのGCグラフを並べてみたので、ぜひとも比較していただきたい。
おそらく、グラフの見え方により2種類のグラフに分類されるだろう。この、見え方の違いがメモリリークを引き起こしていた原因につながるのだ。
読者の中には、もう犯人が分かった人もいるかもしれない。
Copyright © ITmedia, Inc. All Rights Reserved.