Javaアプリケーションのロードされたクラスの数でのメモリリークの可能性
質問
最近、VisualVMを使用して作成しているosgi javaアプリケーションのプロファイリングを開始しました。私が気づいたことの1つは、アプリケーションが(JMSを介して)クライアントへのデータの送信を開始すると、ロードされるクラスの数が一定の割合で増加し始めることです。ただし、ヒープサイズとPermGenサイズは一定のままです。データの送信を停止した後でも、クラスの数は決して減少しません。これはメモリリークですか?ロードされたクラスはどこかに格納する必要があるためだと思いますが、アプリケーションを数時間実行した後でもヒープとpermgenが増加することはありません。
プロファイリングアプリケーションのスクリーンショットについては、こちら
解決
何らかの方法で動的に新しいクラスを動的に作成していますか?
ご協力ありがとうございます。私は問題が何であるかを理解しました。クラスの1つで、Jaxbを使用してXML文字列を作成していました。これを行う際、JAXBはリフレクションを使用して新しいクラスを作成します。
JAXBContext context = JAXBContext.newInstance(this.getClass());
したがって、JAXBContextはヒープ内で言っていませんでしたが、クラスはロードされていました。
プログラムを再度実行しましたが、予想どおり通常のプラトーが見られます。
他のヒント
あなたの問題はバイトコードの生成に関係していると思います。
多くのライブラリは、CGLib、BCEL、Javasist、またはJaninoを使用して、実行時に新しいクラスのバイトコードを生成し、制御されたクラスローダーからロードします。これらのクラスを解放する唯一の方法は、クラスローダーへのすべての参照を解放することです。
クラスローダーは各クラスによって保持されるため、これは、すべてのクラスへの参照も解放しないことを意味します[1]。適切なプロファイラーでこれらをキャッチできます(Yourkitを使用-同じ保持サイズで複数のクラスローダーインスタンスを検索します)
1つの問題は、JVMがデフォルトでクラスをアンロードしないことです(理由は後方互換性です-人々は(間違って)静的初期化子は1回だけ実行されると仮定します。実際には、クラスがロードされるたびに実行されます。)アンロードを有効にするには、次のオプションを使用して渡す必要があります。
-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled
(JDK 1.5でテスト済み)
それでも、過剰なバイトコード生成はお勧めできません。そのため、コードを調べて原因を見つけ、生成されたクラスをキャッシュすることをお勧めします。頻繁な違反者は、スクリプト言語、動的プロキシ(アプリケーションサーバーによって生成されたものを含む)、または巨大なHibernateモデル(この場合、permgenを増やすだけです)です。
参照:
次のように、この動作を理解するのに役立つホットスポットフラグを見つけることができます。
- -XX:+ TraceClassLoading
- -XX:+ TraceClassUnloading
これは良いリファレンスです:
http://java.sun.com/javase/technologies/hotspot /vmoptions.jsp
誤解しない限り、ここではインスタンスではなくロードされたクラスを見ています。
コードが最初にクラスを参照するとき、JVMはClassLoaderを出して、クラスに関する情報を.classファイルなどからフェッチさせます。
どのような条件下でクラスがアンロードされるかわかりません。確かに、静的な情報を持つクラスをアンロードしないでください。
したがって、アプリケーションの実行時にエリアに移動し、新しいクラスを参照する、おおよそのパターンが予想されるため、ロードされたクラスの数は増え続けます。
ただし、2つのことが奇妙に思えます:
- なぜこれほど線形なのですか?
- なぜプラトーにならないのですか?
JVMがプログラムが参照するクラスのほとんどをすでにロードしているため、上昇傾向にあるが、不安定なラインになり、増加に応じて漸減することを期待します。つまり、ほとんどのアプリケーションで参照されるクラスの数には限りがあります。
何らかの方法で動的に新しいクラスを動的に作成していますか?
ベースラインケースを取得するために、同じデバッガーでよりシンプルなテストアプリを実行することをお勧めします。次に、デバッグ情報を出力する独自のClassLoaderの実装を検討するか、レポートを作成するツールがあるかもしれません。
ロードされるこれらのクラスが何であるかを把握する必要があります。
はい、通常はメモリリークです(実際にメモリを直接処理するわけではないため、クラスインスタンスリークのようなものです)。以前にこのプロセスを行ったことがありますが、通常、古いツールキットに追加されたリスナーは、それ自体を削除しませんでした。
古いコードでは、リスナーの関係により「リスナー」が発生します。残るオブジェクト。私は古いツールキットまたは多くの回転を経ていないツールキットを見ます。後のJDKで実行されている既存のライブラリは、参照オブジェクトを認識し、「リスナーを削除」の要件を削除します。
また、毎回再作成する場合は、ウィンドウでdisposeを呼び出します。あなたがそうしなければ彼らが消えることはないと思います(実際には近くの設定で処分もあります)。
SwingまたはJDKリスナーについて心配する必要はありません。これらはすべて参照を使用する必要があるため、大丈夫です。
Eclipse Memory Analyzer を使用して、重複したクラスとメモリリークをチェックします。同じクラスが複数回ロードされる場合があります。
よろしく、 Markus