4

我在我的应用程序中使用 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 不是无状态的。为了解决这个问题,在我的单例弹簧容器中,我将所有 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 查询和 hibernate 创建的别名。但我想知道 Hibernate 缓存(我没有使用二级缓存)如何以及为什么仅在我使用多个 Spring 容器时才会导致问题!
更新
最后我能够指出是什么阻碍了记忆。它是 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 使您的类无状态的另一点是您拥有尽可能少的实例,并在启动时根据需要加载它们。因此,您的范围应该尽可能单例,如果不是,您应该问自己为什么它不是单例,我可以这样做吗?

于 2012-05-07T12:33:22.863 回答