Java JVM垃圾收集器似乎失败了

Java JVM垃圾收集器似乎失败了,java,garbage-collection,out-of-memory,Java,Garbage Collection,Out Of Memory,我们有一个java应用程序,它读取一块数据,但只在短时间内保留该数据。数据存储在“简单”集合中(HashMap,HashSet)。这些集合在处理数据时被清除(因此我调用coll.clear(),而不是coll=null)。循环(读取过程清除)继续,直到“所有数据块”都被处理。一段时间后,会有“新的块”,整个事情会重新开始 此过程已在服务器上运行了数周,没有任何问题。 然而,今天,在计划重新启动之后,它一次又一次地崩溃,并出现OutOfMemoryError:Java堆空间(并通过监视进程自动重新

我们有一个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时,该选项才会起作用