我们有一个java应用程序,它可以读取一大块数据,但只能在短时间内保存这些数据。数据存储在“简单”集合中(HashMap
,HashSet
)。处理数据时会清除这些集合(因此我调用coll.clear()
而不是coll=null
)。循环(读取 - 处理 - 清除)继续,直到“处理所有数据块”。经过一段时间后,会出现“新块”,整个事情又开始了。
此过程在服务器上运行了几个星期没有任何问题。
但是,今天,在计划重新启动之后,它一次又一次地使用OutOfMemoryError: Java heap space
崩溃(并由监控过程自动重启)。
我使用远程调试器和 jvisualvm 工具连接到该进程,以尝试查找是否(以及在哪里)我可能有内存泄漏。在调用clear()
之后,处理线程暂停(由调试器),我使用jvisualvm工具强制gc
。正如我所料,它几乎清除了整个堆(仅使用了4MB)。接下来的周期:相同的行为,在clear
之后几乎没有使用堆等等......最后,该过程不已经不在内存中了!
对我来说,垃圾收集器看起来无法正常工作......
我应该在System.gc()
方法之后调用clear()
吗?
但据我所知(并阅读here),这只是对VM的“建议”;当堆几乎满时,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...
}
}
执行OOMError
方法时发生collector.collectNext()
。
看起来堆仍然包含dataList
循环的上一个迭代的while
变量(和所有数据对象)!
while循环的局部变量不会被垃圾收集,这是正常的行为吗?如果这是真的,我们必须给这个过程几乎两倍于严格需要的内存......
作为hack / check,我在for循环之后添加了一行dataList = null
,但是这并没有改变行为(仍然是OOM
,堆转储仍然显示相同的'双重赋值')
(我想我们很幸运,这个过程没有提前崩溃。)
答案 0 :(得分:0)
我遇到了垃圾收集器在一定时间后出现问题并且出现虚假OOME的问题。当你有一个复杂的具有大量圆圈的物体链时,往往会发生这种情况。解决方案是使用-XX:-UseGCOverheadLimit
标志告诉垃圾收集器不要放弃。
答案 1 :(得分:0)
我遇到了同样的问题,我所做的是:
通过转到Project > Clean > Clean all projects (tick this option)
如果问题仍然存在,则需要重构代码,并消除内存泄漏。