即使没有gc根,WebappClassLoader仍然存在内存泄漏问题。

6

这里是头部转储(更新于2013年10月29日)

我正在开发一个Web应用程序,使用以下技术:

  • Tomcat 7.0.24
  • Java 6
  • Spring 3(带有aop - cglib)
  • SLF4J覆盖Log4j
  • Oracle Coherence

经过大量的工作,我设法消除了所有对类加载器的强引用,现在它可以被垃圾收集器回收。所以,内存泄漏问题解决了吗?当然没有!因为经过几次热部署后,由于PermGen空间不足而出现了OOME。

感谢Yourkit,我能够检查WebappClassLoader是否处于“等待终结”状态(实际上,不是WebappClassLoader本身,而是他的某个引用)。查看内存快照,我发现有几个对Oracle Coherence类的Finalizer引用... enter image description here
这似乎没问题:由于所有的努力都在删除所有强引用(杀死所有协同线程,删除Java安全提供程序等),Coherence对象正在等待被垃圾回收。我认为这里没有什么要做的。
因此,我考虑了一些可能会破坏某些东西并且不允许清空终结器队列的finalize执行。但奇怪的是,使用JMX或jmap -finalizerinfo,终结器队列似乎是空的!所有这些都非常令人困惑,所以我继续在其他地方搜索...

你觉得这里有什么可以做的吗?我读到了一些关于CGLIB增强finalize方法的内容。如果我可以访问Enhancer,我可以像这里所解释的那样创建一个回调过滤器,但我不知道如何在Spring AOP中管理它。

好吧,在其他地方搜索,我发现了几个来自java.lang.reflect.Proxy的弱引用。这些是jdk动态代理吗?还是与内省内存泄漏有关?带有弱引用?

enter image description here

提示: 我正在使用Spring的上下文监听器来刷新内省缓存 (java.beans.Introspector.flushCaches())。 我还能做什么?

让我们继续。

然后,我们有几个来自java.io.ObjectStreamClass$Caches的其他弱引用。 很多我的业务对象都有这些类型的弱引用。

enter image description here enter image description here

也许我需要清除这些缓存。但是怎么做呢?
然后我们还有与com.sun.internal.ResourceManager、java.util.logging.Logging和java.lang.reflect.Proxy相关的弱引用。

enter image description here

我可以用这个弱引用做什么?我需要担心这个问题吗还是问题出在终结器队列上?任何线索都会有所帮助...真的 :-D
啊,另外一件事,我发现一个来自Tomcat“main”线程的弱引用,它永远不会被Tomcat更新。我知道我的应用程序可能会在一些Tomcat线程中留下一些线程本地变量,但Tomcat 7会更新这些线程以避免类加载器内存泄漏
我认为这是我内存快照中最奇怪的东西,但这是一个弱引用,对此我可以做什么?
编辑:阅读{{link2:java.lang.ref javadoc}}后,我找到了这个:
一个对象如果既不是强引用也不是软引用,但可以通过遍历弱引用访问,则该对象为弱可达。当对弱可达对象的弱引用被清除时,该对象就变得可以进行终结了。
所以,当弱引用实现了一个finalize方法时,它们能够保留堆中的对象吗?
同时我找到了一个答案,我设法删除了所有指向我的类加载器的弱引用,但有两个没有被删除:ClassLoaderLogManager.classLoaderLoggers 和与tomcat线程相关的引用。
注意:实际上,我成功删除了第一个引用,但在卸载期间/之后,这个引用会被tomcat重新设置。
编辑:PLUMBR结果
我尝试使用Plumbr,但在Web控制台上没有报告。只有标准输出中的这条消息。
Dumping heap to /opt/tomcat7/headdumps/java_pid9478.hprof ...
Heap dump file created [348373628 bytes in 3.984 secs]
#
# An unexpected error has been detected by Java Runtime Environment:
#
#  Internal Error (javaCalls.cpp:40), pid=9478, tid=1117813056
#  Error: guarantee(!thread->is_Compiler_thread(),"cannot make java calls from the compiler")
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (11.2-b01 mixed mode linux-amd64) [thread 1110444352 also had an error]
# An error report file with more information is saved as:
# [thread 1110444352 also had an error]
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
#
******************************************************************************
*                                                                            *
* Plumbr has noticed that JVM has thrown an OutOfMemoryError: PermGen space. *
*                                                                            *
* You can increase PermGen size with -XX:MaxPermSize parameter.              *
* If you encountered this error after a redeploy, please read next article:  *
* http://plumbr.eu/blog/what-is-a-permgen-leak                               *
*                                                                            *
******************************************************************************

你有OOM发生后的堆转储文件可以提供下载吗?也许我们能够研究一下。 - Nikem
嘿@Nikem!那将非常好!我也尝试过使用plumbr,但没有成功:-(这是转储: https://dl.dropboxusercontent.com/u/9210700/java_pid12165.hprof - ggarciao
你是否尝试在使用Plumbr的情况下重新部署应用程序?如果需要,您可以通过support@plumbr.eu与我们联系,以便调查Plumbr在此情况下出现问题的原因。 - Nikem
我已经安装了Plumbr并成功使用了它,但是没有检测到内存泄漏!我会再试一次。我会随时通知你的! - ggarciao
我建议按照以下步骤进行:在附加了Plumbr的情况下启动您的应用程序,运行一段时间,重新部署应用程序,让Plumbr思考约5分钟。如果这样做没有帮助,请联系我们 :) - Nikem
我已经尝试了Plumbr,但是没有任何效果。即使在OOME之后,我的应用程序也不会出现在Plumbr Web控制台上。也许我错过了什么。你能帮我理解如何使用Plumbr来找到内存泄漏吗?提前感谢! - ggarciao
1个回答

3

我认为Yourkit引导了您走错了方向。

我使用Eclipse Memory Analyzer查看了您的堆转储。它显示WebappClassLoader由类com.inovasoftware.iap.data.access.platform.datarepository.CoherenceDataRepository$$EnhancerByCGLIB$$180c0a4e引用,该实例在某些线程本地变量中仍然存活。一些搜索显示了这个: https://hibernate.atlassian.net/browse/HHH-2481

因此,升级Hibernate版本可能会有所帮助。

MAT screenshot


谢谢这个信息!我现在就去删除那个引用!我会随时通知你的! - ggarciao
嘿@Nikem!感谢你的发现!我设法释放了线程本地,但类加载器仍然处于“待定终结”状态(在yourkit上)。我还与EMA进行了检查,并且只看到一些相干对象的Finalizer引用。如果可以的话,请查看新的头转储https://dl.dropboxusercontent.com/u/9210700/Tomcat-2013-10-25.hprof - ggarciao
我会调查一下。你还是遇到OOM问题吗? - Nikem
很奇怪,我看不到任何重复的类加载器。你的应用程序只有一个WebappClassLoader。这个转储是从几次重新部署中获取的吗?对我来说它看起来非常新鲜。 - Nikem
这是 OOME 后的转储 https://dl.dropboxusercontent.com/u/9210700/java_pid9478.hprof。抱歉耽搁了!(PS:我删除了以前的评论,因为它指向一个不存在的转储) - ggarciao
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接