我正在开发一个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](https://istack.dev59.com/GqZaX.webp)
这似乎没问题:由于所有的努力都在删除所有强引用(杀死所有协同线程,删除Java安全提供程序等),Coherence对象正在等待被垃圾回收。我认为这里没有什么要做的。
因此,我考虑了一些可能会破坏某些东西并且不允许清空终结器队列的
finalize
执行。但奇怪的是,使用JMX或jmap -finalizerinfo
,终结器队列似乎是空的!所有这些都非常令人困惑,所以我继续在其他地方搜索...
你觉得这里有什么可以做的吗?我读到了一些关于CGLIB增强finalize
方法的内容。如果我可以访问Enhancer
,我可以像这里所解释的那样创建一个回调过滤器,但我不知道如何在Spring AOP中管理它。
好吧,在其他地方搜索,我发现了几个来自java.lang.reflect.Proxy
的弱引用。这些是jdk动态代理吗?还是与内省内存泄漏有关?带有弱引用?
提示: 我正在使用Spring的上下文监听器来刷新内省缓存 (java.beans.Introspector.flushCaches()
)。 我还能做什么?
让我们继续。
然后,我们有几个来自java.io.ObjectStreamClass$Caches
的其他弱引用。 很多我的业务对象都有这些类型的弱引用。
然后我们还有与com.sun.internal.ResourceManager、java.util.logging.Logging和java.lang.reflect.Proxy相关的弱引用。
啊,另外一件事,我发现一个来自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 *
* *
******************************************************************************