2013-01-24 10 views
5

私はカスタムクラスローダーでGroovyクラスを動的にリロードするJavaクラスを持っていますが、一部のクラスでは収集されないという奇妙な動作が見られますが、メモリ(例えば、perm genは無期限に成長しません)。私のJavaコードでGroovyクラスは収集されていませんが、メモリリークの兆候はありません

は、私はそう(単純化のために削除定型のもの)のようなクラスをロードしています:

Class clazz = groovyClassLoader.loadClass(className, true, false, true); 
instance = clazz.newInstance(); 

そして私は、クラスローダキャッシュ、metaregistryなどクリアしてGroovyクラスを動的にリロード:

for (Class c : groovyClassLoader.getLoadedClasses()){ 
    GroovySystem.getMetaClassRegistry().removeMetaClass(c); 
} 
groovyClassLoader.clearCache(); 

このコードを繰り返しループしていて、Groovyクラスを常にロードしてから再読み込みすると、奇妙な動作が発生します(テストコードは文字通りリロードプロセスをループしています。作成されたオブジェクトなど、インスタンス上のコードではローカルなのでGCには良いはずです)。

私は128メートルにMaxPermSizeの設定、それを実行した場合、私は行動を漏らす取得し、それがエラーをpermgen OOM:

memory profile for 128m permgen

私は再びそれを実行し、256メートルにMaxPermSizeのを増やす場合は、その後、すべてがあります良い、それは(この画像は1時間ですが、私はリロードの数千人をやって一晩それを実行している)永遠に実行することができます。

enter image description here

誰もが任意の同様の現象に遭遇していますか?または任意のアイデアがありますか?また、最初の例では、メモリ使用量が着実に増加するのではなく、段階的に増加していることは奇妙に思えます。

+0

あなたの256m permダイアグラムは、あなたのプログラムが150M permを実行する必要があることを示しています。私のアプリでは、漏れを防ぐためにgroovyを.classにコンパイルします。しかし、メタクラスはまたpermgen(約100m)の多くを取った – farmer1992

+0

@ farmer1992 - はい、しかし、私はいつでも256シナリオでheapdumpを取る場合は、いくつかの重複したクラスが表示されますが表示されますその時点でロードされている各Groovyクラスの〜20バージョンがあることを確認してください)。また、128グラフでは、行の各ステップが多かれ少なかれリロードに相関しているので、セット全体をリロードすると〜5MBのような形でpermgenが増加することがわかります – rhinds

答えて

3

あなたが見る鋸歯状のパターンは、常に割り当てられて解放されているメモリのために典型的です。キャッシュをクリアするためのコードを追加しましたが、自動的にそのクラスが収集されることは意味しません。 JVMは、長い生きているオブジェクトに対して通常のガベージコレクションを試みる前に、メモリをかなり増やす傾向があります。クラスを削除することはさらに少なくなります。多くの場合、完全なGC実行中にのみ行われます。クラスが収集されたとしても、permgenがいっぱいで、OOMEがスローされるという厄介な状況につながる可能性があります。正確な動作はバージョンによって異なるようです。

とにかく。クラスがもはや参照されていないという理由だけで、クラスがすぐに収集されるわけではありません。代わりに、permgenは最大にまで成長し、クラスをアンロードすることができます。

loadClassの呼び出しのほかに、新しいクラスとそのクラスを参照するメタクラスレジストリが何らかの形で作成される可能性がありますが、参照可能なクラスが増えています。例えば、Groovyのcallsiteキャッシングには、クラスへのSoftReferencesも含まれます。 Reflection(Groovyが行う必要があるかもしれない)によって十分に頻繁に呼び出されたものがある場合、Reflectionをスピードアップするためにヘルパークラスが生成されることがあります。これはGroovyではなく、JDKによって行われます。

一つの修正が必要です。メタクラスは実際のクラスではなく、permgenをとることはできません。しかし、それらはpermgenを取るクラスを参照します。したがって、メタクラスが参照されにくい場合、クラスはそのままです。その参照自体を行うオブジェクトがSoftReferenceの一部であっても、クラスが読み込み不能であると見なすと、そのクラスをハード・リファレンスと見なした興味深い「機能」がIBM JDKにありました。

上記の動作をより完全に説明するために、クラスのロードとアンロードのためにJVMの出力が必要です。私はアプリケーションが理論的には128MBで動作すると仮定します。16時17分30秒前の低点で128MBのグラフを見ると、その前のものが他のものと同じくらい低くないことがわかります。つまり、そのタイムコードの直前のポイントは、以前のポイントよりも多くのクラスをアンロードしました。 JVMはクラスを削除するタイミングを自由に決めることができ、理論的にはアンロードできるすべてのクラスを常に削除するわけではありません。できるだけクラスをアンロードしてパフォーマンスを向上させるには、クラス間でトレードオフを取らなければなりません。

+0

ありがとうございます - 今も同様のパターンが表示されています安定したピーク/トラフが続き、グラフの下の点でインクリメンタルなステップアップが行われるため、GCがオブジェクトを収集する頻度は少なくなりますが、このシナリオでは、 OOMは、permgenの限界近くに位置し、少量をより頻繁に収集します。 GCが使用パターンに基づいて最適化する可能性はありますか? – rhinds

+0

まあ、私は確かに知っていると主張するつもりはないが、私が正しいことを覚えていれば、より頻繁に使用される参照が後で収集されることを理解するために、参照をカウントする使用法のようなものがある。多分それはそのようなものです。しかし、それは本当に憶測だけです。ホットスポットのエンジニアは論理を説明するかもしれませんが、常にそうであるとは限りません;) – blackdrag

関連する問題