过度配置堆上的OutOfMemoryError

在分配一个应该很适合我为JVM提供的堆中的数据结构时,为什么会出现OutOfMemoryError? 这是我最近遇到的一个问题。

确实,当查看开发人员要完成的工作并通过-Xmx参数对提供给JVM的堆大小进行三重检查时,似乎确实存在着一些可疑之处。

30分钟后,我们了解了情况并解开了谜团。 但这确实起初并不明显,所以我认为如果我更详细地描述根本问题,可能会节省一天的时间。

与往常一样,了解问题的最佳方法是通过动手实例。 我构建了一个小的综合测试用例:

package eu.plumbr.demo;
class ArraySize {
	public static void main(String... args) {
		int[] array = new int[1024*1024*1024];
	}
}

代码很简单–它要做的就是分配一个包含十亿个元素的数组。 现在,考虑到java int原语需要4个字节,因此人们可能会认为使用6g堆运行代码可以很好地运行。 毕竟,这十亿个整数应该只消耗4g内存。 那么为什么执行代码时会看到以下内容?

My Precious:bin me$ java –Xms6g –Xmx6g eu.plumbr.demo.ArraySize
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
 	at eu.plumbr.demo.ArraySize.main(ArraySize.java:6)

在投入更多的堆之前(事实上,使用–Xmx7g ,上面的示例运行得很好),让我们尝试了解为什么我们的期望是错误的。

首先– Java中的int原语确实需要4个字节。 因此,这并不意味着我们的JVM实现一夜之间变得疯狂。 而且我可以向您保证,数学运算也是正确的– 1024 * 1024 * 1024 int原语确实需要4,294,967,296字节或4 GB。

要了解发生了什么,让我们运行相同的情况,并通过指定–XX:+ PrintGCDetails来打开垃圾收集日志记录

My Precious:bin me$ java –Xms6g -Xmx6g -XX:+PrintGCDetails eu.plumbr.demo.ArraySize

-- cut for brevity --

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at eu.plumbr.demo.ArraySize.main(ArraySize.java:6)

Heap
 PSYoungGen      total 1835008K, used 125829K [0x0000000780000000, 0x0000000800000000, 0x0000000800000000)
  eden space 1572864K, 8% used [0x0000000780000000,0x0000000787ae15a8,0x00000007e0000000)
  from space 262144K, 0% used [0x00000007e0000000,0x00000007e0000000,0x00000007f0000000)
  to   space 262144K, 0% used [0x00000007f0000000,0x00000007f0000000,0x0000000800000000)
 ParOldGen       total 4194304K, used 229K [0x0000000680000000, 0x0000000780000000, 0x0000000780000000)
  object space 4194304K, 0% used [0x0000000680000000,0x0000000680039608,0x0000000780000000)
 PSPermGen       total 21504K, used 2589K [0x000000067ae00000, 0x000000067c300000, 0x0000000680000000)
  object space 21504K, 12% used [0x000000067ae00000,0x000000067b087668,0x000000067c300000)

答案现在正盯着我们的眼睛:即使我们有很多可用的总堆,堆中的单个区域也没有足够大的空间来容纳4g对象。 我们的6g堆分为四个单独的区域,大小如下:

  • 伊甸园15.36亿
  • 生存空间( )每个256M
  • 老一代4,096M

现在,牢记对象分配必须适合单个区域,我们确实可以看到应用程序没有机会-我们的任何堆区域中都没有足够的空间来容纳此4g分配。

那么–我们现在唯一的希望是进一步增加堆数吗? 即使我们已经提供了将近50%的超额配置-将6g堆交给应该适合4g的数据结构? 没那么快–有替代解决方案可用。 您可以设置内存中不同区域的大小。 它并不像人们期望的那样简单易用,但对启动配置进行两次小的修改就可以解决问题。 使用两个额外的选项启动相同的代码时:

My Precious:bin me$ java -Xms6g -Xmx6g -XX:NewSize=5g -XX:SurvivorRatio=10 eu.plumbr.demo.ArraySize

然后程序执行其工作,并且不会引发OutOfMemoryError。 在启动中添加-XX:+ PrintGCDetails也会对此进行说明:

Heap
 PSYoungGen      total 4806144K, used 4369080K [0x00000006c0000000, 0x0000000800000000, 0x0000000800000000)
  eden space 4369408K, 99% used [0x00000006c0000000,0x00000007caaae228,0x00000007cab00000)
  from space 436736K, 0% used [0x00000007e5580000,0x00000007e5580000,0x0000000800000000)
  to   space 436736K, 0% used [0x00000007cab00000,0x00000007cab00000,0x00000007e5580000)
 ParOldGen       total 1048576K, used 0K [0x0000000680000000, 0x00000006c0000000, 0x00000006c0000000)
  object space 1048576K, 0% used [0x0000000680000000,0x0000000680000000,0x00000006c0000000)
 PSPermGen       total 21504K, used 2563K [0x000000067ae00000, 0x000000067c300000, 0x0000000680000000)
  object space 21504K, 11% used [0x000000067ae00000,0x000000067b080c90,0x000000067c300000)

我们看到,现在的区域大小确实是我们所要求的:

  • 如我们的-XX:NewSize = 5g参数所指定,年轻大小int总数(eden +两个幸存者空间)为5g
  • 如我们用-XX:SurvivorRatio = 10参数指定的,Eden比幸存者大10倍。

请注意,在我们的情况下,两个参数都是必需的。 仅指定-XX:NewSize = 5g仍会以某种方式将其在伊甸园和幸存者之间分割,使得任何区域都无法容纳所需的4g。

希望阅读此说明将为您节省以后的调试时间。 或帮助您避免过度配置资源。

翻译自: https://www.javacodegeeks.com/2014/05/outofmemoryerror-on-overprovisioned-heap.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值