6

カスタムクラスローダーを使用してGroovyクラスを動的にリロードするJavaクラスがあり、一部のクラスが収集されないという奇妙な動作が見られますが、時間の経過とともにメモリがリークすることはありません(たとえば、perm genは無期限に成長し続けません)。

私のJavaコードでは、そのようなクラスをロードしています(簡単にするためにボイラープレートのものは削除されています):

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

次に、クラスローダーキャッシュ、メタレジストリなどをクリアして、Groovyクラスを動的にリロードします。

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

さて、このコードをループして、groovyクラスを絶えずロードしてから再ロードすると、奇妙な動作が見られます(私のテストコードは、文字通りリロードプロセスをループしているだけです-作成されたオブジェクトなどでは何もしません、したがって、上記のコードのインスタンスはローカルであるため、GCに適しているはずです)。

maxpermsizeを128mに設定して実行すると、リーク動作が発生し、OOMpermgenエラーが発生します。

128mpermgenのメモリプロファイル

ただし、もう一度実行してmaxpermsizeを256mに増やすと、すべてが良好で、永久に実行できます(このイメージは1時間ですが、何千ものリロードを実行して一晩実行しました):

ここに画像の説明を入力してください

誰かが同様の行動に出くわしたことがありますか?または何かアイデアがありますか?また、最初の例では、メモリ使用量が着実に増加するのではなく、段階的に増加するのも奇妙に思えます。

4

1 に答える 1

4

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

ともかく。クラスが参照されなくなったからといって、すぐに収集されるわけではありません。代わりに、permgen が最大に成長してクラスがアンロードされる場合があります。

loadClass 呼び出しによって新しいクラスが作成され、メタ クラス レジストリが何らかの方法でクラスを参照することに加えて、参照の可能性があるクラスが他にもあります。たとえば、Groovy でのコールサイト キャッシングには、クラスへの SoftReferences も含まれます。また、リフレクションによって頻繁に何かが呼び出された場合 (Groovy が実行する必要がある場合もあります)、リフレクションを高速化するためにヘルパー クラスが生成される可能性があります。この後者は、Groovy ではなく、JDK によって行われます。

1 つ訂正しなければならないことがあります。メタ クラスは実際のクラスではなく、permgen を使用できません。しかし、それらは permgen を取るクラスを参照しています。したがって、メタクラスがハード参照されている場合、クラスは残ります。IBM JDK には、参照自体を行うオブジェクトが SoftReference の一部であっても、ハード参照されているクラスをアンロード可能と見なす興味深い「機能」がありました。

上記の動作をより完全に説明するには、クラスのロードとアンロードのための JVM の出力が必要です。アプリケーションは理論的には 128MB で実行できると思います。16:17:30 の前の最低点とその前の 128MB グラフを見ると、前のものは他のものほど低くないことに気付くかもしれません。これは、その時点のコードの直前のポイントが、前のポイントよりも多くのクラスをアンロードしたことを意味します。JVM は、いつクラスを削除するかを自由に決定でき、理論上はアンロードできるすべてのクラスを常に削除するとは限りません。クラスが可能なときにアンロードされることと、パフォーマンスとの間でトレードオフが必要です。

于 2014-01-28T15:39:31.210 に答える