4

アプリケーションで Spring フレームワーク (ver 3.0.3) を使用しています。最近、この厄介なjava.lang.OutOfMemoryError: Java heap spaceエラーが発生しました。エラーは実行直後には発生しませんでしたが、アプリケーションの実行から数時間後に発生しました。その時点まで、アプリケーションは完全に正常に動作していましたが、突然 jvm がクラッシュし、メモリ不足エラーが発生しました。
広範囲にわたる調査の結果、この問題は Spring に関係していることがわかりました。Bean を注入する必要があるときはいつでも、すべてのクラスが XMLBeanFactory の新しいインスタンスを作成することに気付きました。つまり、それらはすべて最初に次のコードを持っていました。

XmlBeanFactory beanfactory = new XmlBeanFactory(new ClassPathResource("SpringConfig.xml"));
Bean myBean = beanfactory.getBean("MyBean");

これが推奨されないことを理解しています。Spring コンテナーのインスタンスが 1 つだけ必要であり、すべての Bean 作成要求でそのインスタンスを参照します。そのため、シングルトンを介して SpringFactory を実装したため、常に XMLBeanFactory の単一のインスタンスが作成されます。
上記の変更を行うと、メモリ リークの問題が解決したようです。
私はまだ決心できていません:

  1. Spring コンテナーの複数のインスタンスを使用して Bean を取得したとしても、コンテナーがスコープ外になると、すべての Bean 参照が解放され、すべての Bean がガベージ コレクションに使用できるようになります。メモリリークの問題の原因は何でしたか? 前述したように、シングルトン コンテナーを使用しただけで、メモリ リークがなくなりました。必要に応じて、さらに詳細を提供できます。
  2. 以前に複数のコンテナーを使用した理由は、Bean がステートレスではなかったからです。この問題を解決するために、シングルトン スプリング コンテナーで、すべての Bean スコープをプロトタイプとして作成しました。このアプローチは正しいですか?

更新:
すべての Spring Bean に次のコードを追加した後、興味深い結果が得られました。

protected void finalize()
{
System.out.println(this +" object is garbage collected");
}

各クラスで新しい Spring コンテナーをインスタンス化してから Bean を取得することで、コードを実行しました。上記のコメントは、作成されたほぼすべての Bean に対して出力されました。これは、すべての Spring Bean がクリーンアップされていることを意味します。ただし、使用済みメモリは時間とともに増加し続けました。
すべてのクラスで同じ Spring コンテナーを使用して同じことを行った場合、使用メモリは多かれ少なかれ安定したままでした。これは、Spring コンテナーがメモリを保持していると思わせます。
質問は、(上記のコードで取得した) Spring コンテナーがガベージ コレクションされるのはいつですか? 範囲外に出たらガベージコレクションの対象になると思ってた!!
Hibernate Session Object がリソースをキャッシュしてメモリを保持しているようです。これについてはよくわかりませんが、ヒープダンプ分析では、Hibernate の「文字列」が主要なメモリホルダーとして示されています。一部の文字列には SQL クエリがあり、休止状態で作成されたエイリアスがありました。しかし、複数の Spring コンテナーを使用する場合にのみ、Hibernate キャッシュ (私は 2 次キャッシュを使用していません) がどのように、そしてなぜ問題を引き起こすのでしょうか!
更新:
最後に、何がメモリを保持しているのかを突き止めることができました。これは、Hibernate によって生成されるプライマリ キャッシュです。私たちが持っているように、オブジェクトはクリアされましたgetHibernateTemplate().clear(). ただし、SQL クエリと休止状態のプロパティはセッションごとにキャッシュされ、Spring コンテナーごとに新しいセッションが作成されます。セッションが閉じられるとキャッシュが自動的にクリアされるため、メモリの増加はセッションが閉じられていないことを意味します。getHibernateTemplate().getSessionFactory().close()これは、DAO クラスの最後に明示的に行ったときにメモリの問題が発生しなかったため、さらに検証されています。
懸念事項は、コンテナー自体が範囲外に出ても、Spring Hibernate テンプレートがセッションを閉じないのはなぜですか? コード内のどこでもセッションを明示的に処理していないため、単一のスレッドを実行していても問題は解決しません。フレームワークの実装自体に何か問題があるように感じます。

4

2 に答える 2

4

どの OutOfMemoryError を正確に取得しましたか? だと思いますjava.lang.OutOfMemoryError: PermGen space

もしそうなら、ここに説明があります:

各 Spring コンテナーは異なるクラス ローダーを使用します (ただし、それらはすべて同じ親クラス ローダーを持ちます)。クラスがロードされると、ヒープではなく永続的な世代メモリに配置され、デフォルトで JVM によってガベージ コレクションされることはありません。異なるクラスローダーでロードされた同じクラスは異なると見なされるため、新しい Spring IoC をさらに作成すると永続的な世代がいっぱいになり、最終的にスペースが不足してjava.lang.OutOfMemoryError: PermGen space.

この問題を解決するには、JVM でクラスのアンロード オプションを有効にする必要があります。

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

他のタイプのメモリ不足エラーについては、現時点では説明がありません。スレッドを使用しない限り、Java でメモリ リークを作成することは非常に困難です。

于 2012-05-11T15:41:11.963 に答える
1
  1. あなたのコンテナは本当に範囲外ですか、それとも内部のどこかに登録されていますか?

  2. クラスをステートレスにするSpringのもう1つのポイントは、インスタンスをできるだけ少なくし、起動時に必要に応じてロードすることです。したがって、スコープは可能な限りシングルトンにする必要があります。そうでない場合は、なぜシングルトンではないのかを自問する必要があります。そうすることができますか。

于 2012-05-07T12:33:22.863 に答える