我在我的应用程序中使用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的单个实例
进行上述更改似乎已解决了内存泄漏问题!
我现在还不能下定决心:
更新:
在我将所有代码添加到我的所有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查询和hibernate属性,并且每个spring容器都会创建一个新会话。由于缓存应在会话关闭时自动清除,因此内存增长意味着会话未关闭。当我在DAO课程结束时明确地getHibernateTemplate().getSessionFactory().close()
时,我没有记忆问题,这进一步得到了验证
所以,关注的是,即使容器本身超出范围,为什么spring hibernate模板不会关闭会话?我没有在代码中的任何地方显式处理会话,即使我有一个运行的线程,问题仍然存在。让我觉得框架实现本身出了问题!
答案 0 :(得分:4)
你得到的OutOfMemoryError究竟是什么?我估计它是java.lang.OutOfMemoryError: PermGen space
如果是这样,这里有解释:
每个spring容器使用不同的类加载器(但它们都具有相同的父类加载器)。加载类时,它被放入永久生成内存而不是堆中,默认情况下它们永远不会被JVM垃圾收集。加载了不同类加载器的同一个类被认为是不同的,因此,当您创建更多新的Spring IoC并且最终耗尽java.lang.OutOfMemoryError: PermGen space
的空间时,永久代将被填满。
要解决此问题,应为您的JVM启用类卸载选项:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
对于其他类型的内存不足错误,我目前看不到任何解释。除非使用线程,否则在Java中创建内存泄漏非常困难。
答案 1 :(得分:1)
您的容器是否真的超出范围,或者是否已在内部注册?
Spring让你的类无状态的另一点是你拥有尽可能少的实例,并在启动时根据需要加载它们。所以你的范围应尽可能是单身,如果不是,你应该问自己为什么它不是单身,我可以这样做。