您可以使用-Xmx选项控制java中的最大堆大小。
我们在使用此开关的Windows上遇到了一些奇怪的行为。我们运行一些非常强大的服务器(想想196gb内存)。 Windows版本是Windows Server 2008R2
Java版本是1.6.0_18,64位(显然)。
无论如何,我们遇到了一些奇怪的错误,其中进程因内存不足而退出,即使进程使用的内存少于-Xmx设置指定的内存。
所以我们写了一个简单的程序,每次按下回车键就会分配1GB字节数组,并将字节数组初始化为随机值(以防止任何内存压缩等)。
基本上,发生的事情是,如果我们使用-Xmx35000m(大约35 gb)运行程序,当我们达到25 GB的进程空间(使用Windows任务管理器进行测量)时,我们会遇到内存不足的异常。我们在分配了24 GB的1 GB块(BTW)后点击这个,以便检出。
只需为-Xmx选项指定一个较大的值即可使程序适用于更大量的ram。
那么,发生了什么?是-Xmx只是“关闭”。顺便说一句:我们需要指定-Xmx55000m以获得35 GB的进程空间......
有关正在发生的事情的任何想法?
他们是Windows JVM中的错误吗?
简单地将-Xmx选项设置得更大是否安全,即使-Xmx选项与进程明智之间存在脱节也是如此?
答案 0 :(得分:8)
理论#1
当您使用-Xmx35000m请求35Gb堆时,您实际上说的是允许堆使用的总空间为35Gb。但总空间包括Tenured Object空间(适用于多个GC循环的对象),新创建的对象的Eden空间以及垃圾收集过程中将复制对象的其他空间。
问题是某些空格不能用于分配新对象,也不能用于分配新对象。因此,实际上,您“损失”了35Gb中很大一部分的开销。
有各种-XX选项可用于调整各个空格的大小等。您可以尝试摆弄它们以查看它们是否有所作为。有关更多信息,请参阅this document。 (常用的GC调整选项在第8节中列出。-XX:NewSpace选项看起来很有希望......)
理论#2
这可能会发生,因为您正在分配大型对象。 IIRC,超过一定大小的对象可以直接分配到Tenured Object空间。在你的(高度人为的)基准测试中,这个可能导致JVM没有将东西放入Eden空间,因此能够使用比正常情况更少的总堆空间。
作为一项实验,尝试更改基准以分配大量小对象,并查看它是否设法在OOME之前使用更多可用空间。
以下是我要打折的其他一些理论:
“您遇到了操作系统强加的限制。”我会打折,因为你说你可以通过增加-Xmx ...设置来获得更大的内存利用率。
“Windows任务管理器正在报告虚假号码。”我会对此进行打折,因为报告的数字大致与您认为应用程序已设法分配的25Gb相匹配。
“你正在失去其他东西的空间;例如permgen堆。” AFAIK,permgen堆大小的控制和计算独立于“正常”堆。其他非堆内存使用是一个常量(对于应用程序)或依赖于应用程序执行特定的事情。
“你正在遭受堆碎。”所有JVM垃圾收集器都是“复制收集器”,这个收集器系列具有自动压缩堆节点的属性。
“Windows上的JVM错误。”不大可能。 Windows安装上必须有数万个64位Java才能最大化堆大小。别人会注意到......
最后,如果你不这样做,因为你的应用程序要求你分配大块的内存,并“永远”挂在它上面......你很有可能追逐阴影。 “普通”大内存应用程序不会执行此类操作,并且JVM针对正常应用程序进行了调整...而不是异常应用程序。
如果您的应用程序确实以这种方式运行,那么实用的解决方案就是将-Xmx ...选项设置得更大,只有在您开始遇到操作系统级问题时才会担心。
答案 1 :(得分:2)
要了解您的测量结果,您应该使用一些不同的工具:
procexp
和vmmap
jconsole
(您正在使用现在您应该回答以下问题:
jconsole
对使用的堆大小有何评价?这与procexp
有什么不同?procexp
,0
的值是否会发生变化?答案 2 :(得分:2)
您是否尝试打开GC的详细输出以找出最后一次分配失败的原因。是因为操作系统无法为本机JVM进程分配超过25GB的堆,或者是因为GC对其可以管理的最大内存施加了某种限制。我建议你也使用jconsole连接到命令行进程,看看在分配失败之前堆的状态是什么。此外,像sysinternals进程资源管理器这样的工具可能会提供更好的详细信息,如果它在jvm进程中发生故障。
由于这个过程死于25GB并且你有一代分类收集器,其余几代人可能会消耗10GB。我建议您安装JDK 1.6_u24并使用jvisualvm和visualGC插件来查看GC正在做什么,尤其要考虑所有代的大小,以了解GC / VM内存如何将35GB堆切入不同区域管理器。
如果您不熟悉Generational GC http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html#generation_sizing.total_heap
,请参阅此链接答案 3 :(得分:0)
我认为这与分段堆有关。可用内存可能不是单个连续的空闲区域,当您尝试分配大块时,这会失败,因为请求的内存不能分配到一个单独的区域。
答案 4 :(得分:0)
Windows任务管理器显示的内存是分配给进程的总内存,包括代码,堆栈,perm gen和堆的内存。 使用单击程序测量的内存是运行jvm程序可用的堆jvm量。 通常情况下,Windows为JVM分配的内存总量应该大于JVM作为堆内存可用于程序的内存。