Java JVM垃圾收集器似乎失败了
我们有一个java应用程序,它读取一块数据,但只在短时间内保留该数据。数据存储在“简单”集合中(Java JVM垃圾收集器似乎失败了,java,garbage-collection,out-of-memory,Java,Garbage Collection,Out Of Memory,我们有一个java应用程序,它读取一块数据,但只在短时间内保留该数据。数据存储在“简单”集合中(HashMap,HashSet)。这些集合在处理数据时被清除(因此我调用coll.clear(),而不是coll=null)。循环(读取过程清除)继续,直到“所有数据块”都被处理。一段时间后,会有“新的块”,整个事情会重新开始 此过程已在服务器上运行了数周,没有任何问题。 然而,今天,在计划重新启动之后,它一次又一次地崩溃,并出现OutOfMemoryError:Java堆空间(并通过监视进程自动重新
HashMap
,HashSet
)。这些集合在处理数据时被清除(因此我调用coll.clear()
,而不是coll=null
)。循环(读取过程清除)继续,直到“所有数据块”都被处理。一段时间后,会有“新的块”,整个事情会重新开始
此过程已在服务器上运行了数周,没有任何问题。
然而,今天,在计划重新启动之后,它一次又一次地崩溃,并出现OutOfMemoryError:Java堆空间
(并通过监视进程自动重新启动)
我使用远程调试器和jvisualvm工具连接到该进程,以尝试查找是否(以及在何处)存在内存泄漏。在调用clear()
之后,处理线程立即暂停(由调试器暂停),我使用jvisualvm工具强制执行gc
。正如我所料,它几乎清除了整个堆(只使用了4MB)。下一个循环:相同的行为,在清除之后几乎不使用堆,等等。。。最后,这个过程没有再失去记忆
在我看来,垃圾收集器似乎无法正常工作…
- 我怎样才能证实情况是否如此
- 如果是,这怎么可能
- 我应该在
clear()
方法之后调用System.gc()
但据我所知(和阅读),这只是对虚拟机的一个“建议”;而GC总是在堆几乎满的时候收集所有可能的垃圾;而这样的呼吁应该避免:-)
(我们在Solaris上以服务器模式运行Java 1.6.0_51-b11,没有特殊的GC选项)
分析堆转储后编辑:
我们的代码具有以下结构:
final DataCollector collector = ...
while (!collector.isDone()) {
final List<Data> dataList = collector.collectNext();
for (final Data data : dataList) {
// process data...
}
}
最终数据收集器=。。。
而(!collector.isDone()){
最终列表dataList=collector.collectNext();
用于(最终数据:数据列表){
//进程数据。。。
}
}
执行collector.collectNext()
方法时发生OOMError
堆似乎仍然包含while
循环的上一次迭代的dataList
变量(和所有数据对象)
while循环的局部变量不被垃圾收集是正常行为吗?如果这是真的,我们必须给这个过程几乎两倍于严格需要的内存
作为一种破解/检查,我在for循环之后添加了一行dataList=null
,但这并没有改变行为(仍然OOM
,堆转储仍然显示相同的“双重赋值”)
(我想我们很幸运,这个过程没有在早些时候崩溃。)我遇到了垃圾收集器在一段时间后退出并出现错误OOMEs的问题。当您有一个包含许多圆的复杂对象链时,这种情况往往会发生。解决方案是使用-XX:-usegcoveredlimit
标志告诉垃圾收集器不要放弃。我也遇到过同样的问题,我所做的是:
通过以下步骤清理项目:project>Clean>Clean all projects(勾选此选项)
如果问题仍然存在,您需要重构代码,并消除内存泄漏。发布OutOfMemoryError
的整个堆栈跟踪;它可以有其他有用的消息。“整个stacktrace”是不可能的:进程崩溃了+40次,每次都有一个完全不同的堆栈跟踪(除了main()
之后的5个调用)。I.m.o.它也没用:发生OOMError
的确切代码几乎可以是任何东西,在这里我看到它发生在java.nio.ByteBuffer.wrap
,com.sybase.jdbc4.utils.BufferPool.makeBuffer
,java.util.GregorianCalendar.computeFields
,java.util.jar.Manifest$FastInputStream.
,中,等等,@chrylis:Java堆空间
,如果你是这个意思的话。更新了原始问题。您确定没有任何更改吗?一些Java更新或应用程序的新版本?这是无法解释的,所以一切都必须考虑。作为一个黑客,我会尝试给进程更多的内存,我肯定会尝试System.gc()
(待问题解决后再删除它)。当然,gc可能会被破坏。然而,您自己的代码被破坏的可能性更大。添加-XX:+heapdumponotofmemoryerror
并使用Ecilpse MAT分析堆转储。我不同意两次:1。GC对于复杂的链没有问题,特别是在不可访问的情况下(GC并没有真正收集垃圾,而是拾取非垃圾,剩下的是可用内存)。2.这并不是真的假的,这是一种收集内存需要很长时间,而收益很小的情况。这意味着应用程序将很难运行。@maaartinus这是Oracle/Sun针对各种错误建议的解决方法。特别是有一个令人讨厌的SoftReference对象导致东西永远不会被垃圾收集,但还有其他各种情况。其中许多已经在较新的JVM中修复/调整,但OP显然使用的是较旧的JVM,而这些JVM中可能没有修复。一旦GC在第一次顺利运行时克服了“复杂性”。它不会像您建议的那样导致应用程序冻结。我知道SoftReference
错误,但不知道其他错误。每次我看到“GC开销太高”,当被抑制时,事情只会变得更糟。但我的经验有限,所以你可能是对的。但是,OP得到了“Java堆空间”,这似乎是另一个问题。@Necreaux:就我所能找到的关于该选项的内容而言,只有当错误消息是GC开销l时,该选项才会起作用