Java -Xmx在大量ram上的奇怪行为

时间:2011-03-07 23:00:27

标签: java windows

您可以使用-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选项与进程明智之间存在脱节也是如此?

5 个答案:

答案 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)

要了解您的测量结果,您应该使用一些不同的工具:

  1. Windows任务管理器(我只知道Windows XP,但我听说有传言说任务管理器从那时起就有所改进。)
  2. 来自Sysinternals的
  3. procexpvmmap
  4. 来自JVM的
  5. jconsole(您正在使用 Sun Oracle HotSpot JVM,不是吗?)
  6. 现在您应该回答以下问题:

    • jconsole对使用的堆大小有何评价?这与procexp有什么不同?
    • 如果用非零数字填充字节数组而不是将它们保留在procexp0的值是否会发生变化?

答案 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作为堆内存可用于程序的内存。