我正在使用java -Xmx240g mypackage.myClass
操作系统是Ubuntu 12.10。
top
说MiB Mem 245743 total
,并且表明java流程从一开始就有virt 254g
,res
正稳步增长到169g
。那时看起来它开始垃圾收集很多,我想是因为该程序在那时是单线程的,CPU%
大部分是100%
到目前为止,并且它跳到1300左右此时为-2000(我认为它是多线程垃圾收集器),然后res
慢慢移动到172g
。那时java崩溃了
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
在new double[2000][5]
java -version
说
java version "1.7.0_15"
OpenJDK Runtime Environment (IcedTea7 2.3.7) (7u15-2.3.7-0ubuntu1~12.10)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)
硬件是亚马逊cr1.8xlarge实例
在我看来,即使有大量可用内存,java也会崩溃。这显然是不可能的,我必须解释一些错误的数字。我应该在哪里了解发生了什么?
编辑:
我没有指定任何GC选项。唯一的命令行选项是-Xmx240g
我的程序成功处理了许多输入,top
有时表示它使用了高达98.3%的内存。但是我通过某些程序输入重现了上述情况。
EDIT2:
这是科学的应用。它有巨大的树(1-10百万个节点),在每个节点中有几个double
数组,大小约为。 300x3 - 900x5。初始树创建程序后没有分配太多内存。大多数情况下,这些数组都会进行一些算术运算。
EDIT3:
HotSpot JVM以同样的方式死亡,在170-172g标记处使用了大量CPU并且崩溃了同样的错误。看起来70-75%的内存是JVM不想跨越的神奇线路。
最终解决方案: 使用-XX:+ UseConcMarkSweepGC -XX:NewRatio = 12程序通过170g标记,并且正在愉快地工作。
答案 0 :(得分:8)
您需要做的第一件事就是获取堆转储,以便在JVM崩溃时确切了解堆的样子。将这组标志添加到命令行:
-XX:+HeapDumpOnOutOfMemoryError -verbose:gc -XX:+PrintGCDetails
发生崩溃时,JVM会将堆写入磁盘。坦率地说,它需要花费很长时间才能达到这么大的规模。如果您已经在运行Eclipse,请下载Eclipse MAT或安装插件。从那里,您可以加载堆转储并运行几个预制报告。您需要检查泄漏嫌疑人和支配者树以查看记忆的去向并确定您没有实际泄漏。
在那之后,我会向你推荐Oracle read this document关于垃圾收集的问题,不过这里有一些你可以考虑的事情:
-XX:+UseConcMarkSweepGC
我从来没有听说有人在堆大小的堆上使用并行唯一的收集器。您可以激活并发收集器,并且您需要阅读增量模式并确定它是否适合您的工作负载/硬件组合。
-XX:MinHeapFreeRatio=25
当您执行完整收集时,请将其调低以降低垃圾收集器的栏。这可能会阻止您执行完整集合时耗尽内存。 40%是默认值,尝试使用较小的值。
-XX:NewRatio
我们需要了解更多有关您的实际工作量的信息:这是一个webapp吗?摇摆应用程序?根据预期对象在堆上保持活动的时间长短将对新比率值产生影响。像你正在运行的服务器模式虚拟机默认情况下具有相当高的新比率(8:1),如果你有很多长期存在的对象,这可能不适合你。
答案 1 :(得分:1)
作为一般建议,不要使用OpenJDK,对生产环境来说更少,它比Sun / Oracle的要慢得多。
除此之外,我从未见过使用过多内存的虚拟机,但我想这就是你所需要的(或者你的代码使用的内存比你需要的多?)
编辑:服务器的OpenJDK很好,只有与Sun / Oracle JDK的区别在于桌面内容(声音,gui ......)所以忽略那部分。答案 2 :(得分:1)
如果我理解你的问题,看起来在程序到达行new double[2000][5]
之前实际发生了内存泄漏。当线路被击中时,内存似乎已经很低,因此当该线路要求更多内存时它会抛出。
我会使用jvisualvm或类似的工具来找出内存泄漏的位置。内存泄漏我遇到的主要是在循环中创建字符串,缓存未被清除等等。