4

職場では、いくつかの webapps を実行するいくつかの tomcat サーバーがあり、その約半分は画像処理を行う必要があります。

画像処理を行う前に、これらの Web アプリケーションはImageIO.scanForPlugins()、適切な画像リーダーとライターをメモリに取り込むために a を実行します。これまではイメージの処理が必要なときにいつでも実行されていましたが、現在は webapps が初期化されている場合にのみスキャンを実行します (実行後に jar を追加しないため、スキャンを複数回実行する必要はありません)。

数日後、Tomcat インスタンスが原因でクラッシュしましたOutOfMemoryError。幸いにもHeapDumpOnOutOfMemoryErrorオプションが設定されていたので、ヒープ ダンプを調べました。ダンプでは、メモリの 97% が のインスタンスによって使用されていることがわかりましたjavax.imageio.spi.PartialOrderIteratorjava.util.LinkedListそのスペースのほとんどは、 1,800 万の要素を持つバッキングによって占められていました。リンクされたリストはjavax.imageio.spi.DigraphNode、によってロードされたイメージ リーダーとライターを含む で構成されImageIO.scanForPlugins()ます。

「あはは」、「どこかでループ内でスキャンを実行しているに違いなく、同じ要素を何度も何度も追加しているだけだ」と思いました。しかし、この仮定を再確認する必要があると考えたので、次のテスト クラスを作成しました。

import javax.imageio.ImageIO;

public class ImageIOTesting {

public static void main(String[] args) {

    for (int i = 0; i < 100000; i++) {
        ImageIO.scanForPlugins();
        if (i % 1000 == 0) {
            System.out.println(Runtime.getRuntime().totalMemory() / 1024);
        }
    }
}
}

しかし、このクラスをサーバー環境で実行すると、メモリの使用量はまったく変わりません。

javax.imageio パッケージのソースをざっと調べてみると、サービス プロバイダーが既に登録されているかどうかをスキャンがチェックし、登録されている場合は新しいプロバイダーを登録する前に古いプロバイダーの登録を解除することがわかります。ここで問題となるのは、なぜこの巨大なサービス プロバイダーのリンク リストがあるのか​​ということです。なぜそれらは有向グラフとして保存されるのですか? さらに重要なことに、これが起こらないようにするにはどうすればよいですか?

4

2 に答える 2

3

古い質問への遅い回答ですが、とにかく:

ImageIOプラグイン レジストリ ( ) は「VM グローバル」であるためIIORegistry、デフォルトではサーブレット コンテキストではうまく機能しません。これは、OP のように、WEB-INF/libまたはフォルダーからプラグインをロードする場合に特に顕著です。classes

サーブレット コンテキストは、(コンテキストごとに新しいクラス ローダーを使用して) クラスを動的にロードおよびアンロードします。アプリケーションを再起動すると、古いクラスはデフォルトで永久にメモリに残ります (次回scanForPluginsが呼び出されると、ClassLoaderクラスをスキャン/ロードするのは別のクラスになるため、それらはレジストリ内の新しいインスタンスになります。テスト コード ループでは、同じClassLoader常に使用されるため、インスタンスが置き換えられ、実際の問題は決して現れません)。

このリソース リークを回避するには、 を使用して、ContextListenerこれらの「コンテキスト ローカル」プラグインが明示的に削除されていることを確認することをお勧めします。これは、プラグインIIOProviderContextListenerの動的なロードとアンロードを実装するいくつかのプロジェクトで使用した例です。ImageIO

于 2013-06-28T12:58:57.713 に答える
0

私には醜いメモリ リークのように見えます。コード全体を見ないとどこにあるのか推測できませんが、何を注意深く確認する必要があるかはわかっています。

考えられる原因:

_グローバル変数リスト、要素を追加し、削除しないでください。

_Infinite/big ループ、大量の要素をリストにロード、削除しないでください。

また、 VisualVMなどの Java プロファイラーを使用すると役立ちます。

それがどのように機能するかをよりよく理解するために、より多くの情報を提供していただければ、回答を改善できるかもしれません。より多くの情報がなければ何もできません =)

それが役に立てば幸い!

于 2012-09-10T19:24:19.423 に答える