解放されないリフレクションクラス
ある基幹システムを、Tomcat+Postgres+Tapestry+Seasar2+S2Tapestry+S2Daoの構成で開発したが、数日間連続稼動していると、パーマネント領域(以下Perm)がジワジワと減っていくという現象に悩まされている。Tenuredは十分ありGC/FullGC時の下限の平均は100MB前後に落ち着いている。にも関わらずPermは減るのだ。ヒストグラムを取ると判別可能クラス数は減っている時間帯でも、判別不可能クラス数は増加している。
ヒストグラムで取得できるクラス名を便宜上、判別可能クラス、判別不可能クラスという言い方にしている。判別可能なのはクラス名称が明らかなもの。javaasistによりEnhancedされたものも、AOPアライアンスでEnhancedされたものも判別可能としている。これに対してリフレクションによる、sun.reflect.GeneratedConstructorAccessor+ユニークな番号、sun.reflect.GeneratedMethodAccessor+ユニークな番号というクラス名で定義されるものを判別不可能としている。
最初はTapestryなどがポコポコEnhancedなページクラスやコンポーネントクラスを生成していることが原因ではないかと疑ったのだが全く問題なかった。一度作成したEnhancedなページクラス(およびその中に含まれる使用Enhancedなコンポーネントクラスも含む)は、リセットサービスが実行されない限り、一定のクラス名を保っているようだ。では増減した判別可能クラスを比較してコンストラクタの数やメソッドの数に大きな違いがあるのかと言えばそうではないようだった。
起動してから3日後と4日後で比較した。時間帯によるものかたまたま4日後の方がロードされているページクラスが少なかった。ヒストグラムを取得した結果は以下だ。
3日後 | 4日後 | |
---|---|---|
インスタンス数合計 | 2440506 | 2387520 |
使用メモリ合計 | 170369928 | 148049312 |
クラス合計 | 11810 | 12454 |
GeneratedConstructorAccessor数: | ||
695 | 770 | |
GeneratedMethodAccessor数: | ||
8051 | 8720 |
いろいろな可能性を探りながら調査しているが未だ原因が突き止められていない。深刻だ。ふと、実在クラスが解放されたあともリフレクションメソッドをキープしているものがいるのではないかという疑念が湧いた。構造上のメモリリークが起きているのではなかろうか。特にリフレクション呼び出しの固まりのようなOGNL辺りの挙動が気になる。
運用上、稼動中にページやHTMLテンプレートの差し替えが1〜2日に1回程度の割合で起きる。そのためリセットサービスは実行されているのだが、この際にページクラスやコンポーネントなどは参照が消えて消えるクラスの対象となるが、OGNLの中ではメソッドアクセッサやコンストラクタアクセッサを一度持ってしまうとキャッシュしつづけるのではないか?
手繰り寄せる糸が一本も無くなってしまって、この文書をつづり始めたのだが、この可能性を探ってみることにする。