垃圾回收Spring容器

15 浏览
0 Comments

垃圾回收Spring容器

我在我的应用程序中使用了Spring框架(版本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容器的实例,并将这个实例用来处理所有bean创建请求。因此,我通过单例实现了SpringFactory,从而始终创建单一实例的XMLBeanFactory。

上述更改似乎解决了内存泄漏问题!

我还没有能够下定决心的是:

  1. 即使我使用多个spring容器实例来获取bean,当容器超出作用域时,它应该释放所有bean引用,使所有bean都可用于垃圾回收。那么是什么导致了内存泄漏问题?正如我提到的,只使用单例容器就使内存泄漏问题消失了。如果需要的话,我可以提供更多的细节。
  2. 我们之前使用多个容器的原因是因为这些bean并不是无状态的。为解决这个问题,在我的单例Spring容器中,我将所有的bean作用域都设为原型。这种方法正确吗?

更新:

在我给所有的Spring bean添加了以下代码之后,我得出了有趣的发现:

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

通过每个类实例化一个新的Spring容器,然后获取bean来运行代码。上面的注释几乎适用于创建的所有bean。这意味着所有的Spring bean都得到了清理。然而,随着时间的推移,使用的内存不断增加。

当我让所有的类使用同一个Spring容器时做了同样的事情,使用的内存保持稳定。这让我想到Spring容器正在占用内存。

那么问题是,当一个Spring容器(如上述代码所示)会被垃圾收集?我认为一旦它超出范围,它就有资格进行垃圾收集!

似乎Hibernate Session对象正在缓存资源并且占用内存。我不确定这一点,但是堆转储分析显示Hibernate“字符串”是主要的内存持有者,例如,一些字符串有SQL查询和Hibernate创建的别名。但是我想知道为什么Hibernate缓存(我不使用第二级缓存)只会在我使用多个Spring容器时引起问题!

更新:

最终,我成功地确认了是什么占用了内存。这是Hibernate生成的主缓存。由于有getHibernateTemplate().clear(),对象都被清除了。然而,每个会话缓存的sql查询和Hibernate属性都会被缓存,并且每个Spring容器都会创建一个新会话。由于当会话关闭时缓存应该自动清除,而内存增长意味着会话没有被关闭。这一点得到了进一步证实,因为在我的DAO类结尾时,当我明确执行了getHibernateTemplate().getSessionFactory().close()时,我没有内存问题。

因此,问题是,为什么Spring Hibernate模板即使容器本身超出范围,也没有关闭会话?我在代码中没有明确处理会话,即使是在单个线程运行时,问题也会持续存在。这让我觉得框架实现本身有问题!

admin 更改状态以发布 2023年5月24日
0
0 Comments
  1. 你的容器真的超出了范围吗,还是被内部某个地方注册了?

  2. Spring中将类设计为无状态的另一个重点在于你尽可能拥有尽可能少的实例,并在启动时根据需要加载它们。因此,你的作用域应该尽可能地是单例,如果不是,你应该问自己为什么不是单例,并且我能使它成为单例吗。

0
0 Comments

你确切地遇到了哪种OutOfMemoryError?我猜测它是 java.lang.OutOfMemoryError: PermGen space

如果是这样,这里是解释:

每个Spring容器使用不同的类加载器(但它们都有相同的父类加载器)。当一个类被加载时,它被放置在永久代内存中,而不是堆内存,并且默认情况下JVM不会垃圾回收它们。使用不同类加载器加载的相同类被视为不同的类,因此,永久代在创建更多新的Spring IoC时被填满,并最终耗尽空间,导致 java.lang.OutOfMemoryError: PermGen space 产生。

要解决这个问题,应该为JVM启用类卸载选项:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

对于其他类型的内存不足错误,我目前看不到任何解释。在Java中很难创建内存泄漏,除非您使用线程。

0