我有一个内存转储,我是从一个垂死的应用程序。它消耗了所有可用堆(-Xmx1024m)。它使用com.gargoylesoftware.htmlunit.WebClient
来抓取网页。每分钟发出一些http请求,几天后就会死掉。正如我从转储中看到的那样,它有~1750个HtmlPage
类的实例,每个实例都包含相关对象的音调,包括已爬网页面的完整内容。
我无法理解为什么HtmlPage
不是垃圾收集的。我已经调查了实例引用,我没有看到任何我的代码持有对它的引用,并且VisualVM说“找不到GC根”。据我所知,它应该意味着该对象符合gc的条件,但它不起作用。
应用程序作为一个简单的独立进程运行,它不使用任何Web容器或应用程序服务器。
任何提示?我还应该研究什么?
功能
- htmlunit v2.7
- java版“1.6.0_13” Java(TM)SE运行时环境(版本1.6.0_13-b03) Java HotSpot(TM)服务器VM(版本11.3-b02,混合模式)
- Linux my.lan 2.6.18-128.el5#1 SMP Wed Dec 17 17:42:39 EST 2008 i686 i686 i386 GNU / Linux
UPDATE1
我试图通过YourKit Java Profiler分析转储。它向我展示了许多保留大小为310mb的java.lang.ref.Finalizer
个对象。它们是为net.sourceforge.htmlunit.corejs.javascript.NativeGenerator#finalize()
终结器创建的,NativeGenerator
指的是Window
,然后是HtmlPage
和所有内容。
有人知道他们为什么留在记忆中?
注意:好奇,但VisualVM显示“待定完”为零。
答案 0 :(得分:1)
确保在完成页面后调用webClient.closeAllWindows() - 否则JavaScript线程将继续运行,并保留对页面资源的引用等。
答案 1 :(得分:1)
当一个对象具有非常重要的finalize()方法时,在创建对象的实例时,JVM会创建java.lang.ref.Finalizer,它保存对创建的对象的引用,因此它不会在finalize()方法之前收集垃圾完了。内存泄漏来自那些没有按时清除的java.lang.ref.Finalizer-s。清除这些终结器是由一个具有较低优先级的单独的终结器守护程序线程完成的,因此如果使用已实现的finalize()方法创建大量对象实例,则会及时耗尽内存。
它描述的非常好:
http://www.fasterj.com/articles/finalizer2.shtml
这是他们建议的解决方案:
“一个显而易见的方法是增加”Finalizer“守护程序线程的优先级 - 没有这个API,所以你必须遍历所有线程才能按名称找到它,然后增加它的优先级。”
祝你好运