7

私が働いている場所では、アプリケーションの 1 つで JVM のヒープ領域が不足するという問題が発生しています。プロファイラーでヒープダンプを調べるなど、この原因をいくつか検索しましたが、今はほとんど行き詰まっています。

まず、問題のシステムについて少し説明します。これは、Spring と Hibernate を使用して組織に関する記録を保持する Java アプリケーションです。このシステムは、このタイプのデータを担当する政府機関から組織に関するデータを取得するために使用される Web サービス クライアントのセットで構成されています。さらに、システムはそのようなデータを含むローカル データベースを保持し、Web サービス呼び出しのキャッシュとして機能するため、組織に関する情報が初めて要求されたときにローカルのリレーショナル データベースに保存され、これが検索に使用されます。次のリクエストのデータの。Hibernate は、このデータベースとの通信に使用されます。

前に示したように、問題は、しばらくすると、アプリケーションが OutOfMemoryError: java heap space でクラッシュし始めることです。Eclipse+MAT を使用してヒープ ダンプを調べたところ、犯人は Hibernate のものSessionFactoryObjectFactoryであり、割り当てられたメモリの約 85% を占めていることがわかりました (すべてメモリが保持されています)。この中に保持されているオブジェクトの種類を正確に特定するのは少し難しいことがわかりました。最上位には Glassfish がありWebappClassLoader、これにはorg.hibernate.impl.SessionFactoryObjectFactory. これにはorg.hibernate.util.FastHashMapが含まれており、これには が含まれていjava.util.HashMapます。これには多数のエントリが含まれており、それぞれに HashMap エントリ、a、org.hibernate.impl.SessionFactoryImplおよび String が含まれています。HashMap エントリには、同じ 3 つのオブジェクト、HashMap エントリ、SessionFactoryImplおよび文字列であり、この構造は何度も繰り返されます。s には、多数のオブジェクトが含まれています。SessionFactoryImpl特にorg.hibernate.persister.entity.SingleTableEntityPersister、多数の String と HashMap が含まれています。一部の文字列はドメイン オブジェクトの変数を参照し、一部は sql ステートメントを含みます。

一見すると、このオブジェクトが不必要な量のメモリを占有しているように見えた (ダンプ ファイルは 800MB で、そのうち 650MB は によって占められていたSessionFactoryObjectFactory)。そのため、オブジェクトのロードとアンロードのログを有効にし、システムにデータを要求しようとした。組織 (別のシステムからの Web サービス呼び出しを介して)。ここで気付いたのは、オブジェクトのロードに関するメッセージがたくさんあるのに、アンロードされたオブジェクトに関するメッセージがほとんどないことです (ライブラリ オブジェクトのアンロードだけがありました)。これにより、オブジェクト (組織など) がメモリにロードされると、アンロードされることはありません。つまり、時間が経つにつれて、システムはメモリを使い果たします。(これは、ログで見つかった内容に基づく公正な仮定ですか?)

そして、その原因を突き止めようとしましたが、これは非常に困難でした。Hibernate によってロードされたオブジェクトはセッションが存続する限り存続するため、Spring の への呼び出しを に置き換えることで、セッションの処理方法を変更しようとしHibernateDaoSupport#getSession()ましたHibernateDaoSupport#getSessionFactory().getCurrentSession()。これによる問題への明らかな影響はありませんでした。また、問題の Dao メソッドのいくつかの finally ブロックに ... への呼び出しを追加しようとしましたが、見た目にも影響はありませんでしgetCurrentSession().flush()た。.clear()(Dao メソッドはすべて で注釈が付けられています@Transactional。これは、セッションがメソッド内でのみ有効であることを意味し@Transactional、メソッドへの連続した呼び出しはgetCurrentSession()(?) を呼び出すときに異なるセッションを取得する必要があることを意味します)

そのため、チェックする他の領域を考え出すことになると、今はほとんど行き詰まっています. どこを見て、何を探すべきかについて、誰かがアイデアや指針を持っていますか?

ヒープダンプは のインスタンスがたくさんあることを示しましたorg.hibernate.impl.SessionFactoryImpl。これは予想どおりですか? (私は、SessionFactory のインスタンスが 1 つだけ、またはいくつかのトップがあるべきだと考えていたでしょう。)

編集:

私は実際に問題を解決できたと思います:

他のオブジェクトへの依存関係が webservice-class で処理される方法が問題であることが判明しました。ClassPathXmlApplicationContext(...)これは、webservice クラスのコンストラクターでnew を呼び出すことで解決されました。これにより、リクエストごとに (または少なくともセッションごとに) 多くのオブジェクトがロードされ、再度アンロードされることはありませんでした (主に Hibernate のSessionFactoryImpl. 代わりに依存関係を注入するように webservice-classes を変更し、これまでにプロファイラーを使用して見たものを形成すると、複数のSessionFactoryImpl-objects の問題が解決されました。

GlassFish 2.x から GlassFish 3.x にアップグレードすることで問題が悪化した可能性があると思います。これは、webservice-class のインスタンス化方法に関するいくつかの違いである可能性があります。

4

1 に答える 1

5

質問自体ではなく、回答にこの問題の解決策を追加したほうがよいでしょう。

ここでの原因は、さまざまなオブジェクト、特にWebサービスクラスでのスプリングビーンのロードがどのように行われたかでした。これは、

新しいClassPathXmlApplicationContext(...)

個々のWebサービスクラス。これを行うと、ロードされたオブジェクトがガベージコレクションされないようにするという厄介な副作用があります(Springの内部の一部によって参照されているためだと思います)。Glassfishバージョンの変更により、webservice-objectsのインスタンス化に何らかの影響があり、新しいthisへの呼び出しが増え、いっぱいになってクラッシュするまで、メモリを占有するジャンクオブジェクトが増えたようです。

この問題の解決策は、通話をに移動することでした

新しいClassPathXmlApplicationContext(...)

静的ファクトリパターンを使用して、次のような別のクラスに出力します。

public class ContextHolder {
    private static ClassPathXmlApplicationContext context;

    public static getSpringContext() {
        if (context == null) {
            context = new ClassPathXmlApplicationContext("applicationContext.xml");
        }
        return context;
    }
}

そして、これを新しいClassPathXmlApplicationContextの代わりに、webservice-classesで呼び出します。

アップデート:

ClassPathXmlApplicationContextCloseable/Autocloseableなので、try-with-resource別の可能性もあります。

try (final ClassPathXmlApplicationContext applicationContext =
             new ClassPathXmlApplicationContext("applicationContext.xml")) {
    //do stuff
}
于 2012-12-07T13:52:57.777 に答える