2014-09-27 21 views
2

我有一块混杂着Scala代码的复杂Java代码,它读入输入文件,并在逐行处理行时创建大量数据结构,包括输入中长度最大为10的所有子字符串的散列表。Java在产生OutOfMemory错误之前等待了20个小时?

我最初使用-Xmx4g开关运行代码,代码花费了20小时,然后才返回OOM错误,但没有完成整个文件。

然后我运行代码-Xmx32g,代码处理文件中的所有行,并在8分钟内,然后继续处理读取的数据结构。 8分钟后,Java使用的驻留内存大约为21GB。

我的问题是:为什么Java在8分钟内没有返回OOM错误?它在20小时内做了什么?

+5

不断垃圾收集。 – 2014-09-27 19:54:44

+0

'String.substring()'的内存消耗高度依赖于Java版本(在1.7.0_06之前或之后),以及您的子字符串是否覆盖原始字符串的大部分或仅包含原始字符串的一小部分。 Pre'1.7.0_06'子字符串由原始字符串的相同char []支持 - 整个字符串保留在内存中,但是被所有子字符串共享。用'1.7.0_06'子串创建新的字符数组。 – 2014-09-27 21:49:37

+0

@FabianBarney:我想知道他们为什么这么做。旧的方法似乎更明智。 – 2014-09-27 22:35:09

回答

3

根据您的描述,我敢打赌,您遇到的OutOfMemoryError被标记为“java.lang.OutOfMemoryError:GC overhead overhead exceeded exceeded”。独立,如果我的赌注是正确的,形势的描述完全一致到什么下面发生的事情:

  • 您加载一些数据到内存
  • GC踢和清理一些。在每次GC完成它的工作时,它暂停了应用程序线程。
  • 您加载了一些,触发了更多的GC,但在每次GC之后,恢复的内存越来越少。
  • 垃圾回收的频率增加了,直到很少有真正的进展
  • JVM有一个内置的安全网来捕捉这些情况 - 每当你花费超过98%的GC时间并且只能恢复2%或更少的堆,则抛出“超出java.lang.OutOfMemoryError:GC开销限制”错误。

要检测这种情况,可以打开GC日志(例如,-verbose:gc或-XX:+ PrintGCDetails),并留意暂停时间。

+0

)谢谢!您能否提供有关JVM“安全网”的更多详细信息?在什么时间范围内是“ 98%“计算?最后5分钟,最后一小时?这是JVM的最新补充吗? – Sarkom 2014-09-30 15:36:28

+1

在Oracle公共文档中,没有详细提及所用的时间段 - 所以真相的最佳来源是挖掘OpenJDK源代码代码来找到答案,但这不是最近的补充,至少在JDK6中已经出现了 – Ivo 2014-09-30 21:24:54

0

Java有一个垃圾收集器线程。如果它发现它可以收集足够的垃圾来释放空间,它会这样做。

因此,我建议使用visual VM并监视下次垃圾收集的时间。

1

垃圾收集是非确定性和复杂的。结合JIT编译等其他内容,精确的内存使用模式很难预测和复制。

你提到它包括Scala?这使事情变得更有趣。 “好”的功能结构可能会产生比看起来更多的垃圾。即使像在Option-s中包装对象那样的小事情也需要额外的内存来清理。