java - 为什么当 20% 的堆空闲时我会得到 OutOfMemory?

标签 java garbage-collection jvm-hotspot

我已将最大堆设置为 8 GB。当我的程序开始使用大约 6.4 GB(如 VisualVM 中的报告)时,垃圾收集器开始占用大部分 CPU,并且程序在进行约 100 MB 的分配时因 OutOfMemory 而崩溃。我在 Windows 上使用 Oracle Java 1.7.0_21。

我的问题是是否有 GC 选项可以帮助解决这个问题。除了 -Xmx8g,我没有传递任何东西。

我的猜测是堆越来越碎片化,但 GC 不应该压缩它吗?

最佳答案

收集点点滴滴的信息(这出乎意料地困难,因为 official documentation 非常糟糕),我确定...

发生这种情况的原因通常有两个,都与可用空间碎片有关(即,可用空间以小块形式存在,因此无法分配大对象)。首先,垃圾收集器可能不会进行压缩,也就是说它不会对内存进行碎片整理。即使是进行压缩的收集器也可能做得不好。其次,垃圾收集器通常将内存区域拆分为它为不同类型的对象保留的区域,并且它可能不会考虑从拥有它的区域中获取空闲内存以提供给需要它的区域。

CMS 垃圾收集器不进行压缩,而其他垃圾收集器(串行、并行、parallelold 和 G1)进行压缩。 Java 8 中的默认收集器是 ParallelOld。

所有垃圾收集器都将内存分成多个区域,据我所知,它们都懒得努力防止 OOM 错误。命令行选项 -XX:+PrintGCDetails对于一些 Collection 家来说,显示区域的大小以及它们有多少可用空间非常有帮助。

可以尝试不同的垃圾收集器和调整选项。关于我的问题,G1收集器(使用 JVM 标志 -XX:+UseG1GC 启用)解决了我遇到的问题。然而,这基本上是偶然的(在其他情况下,它 OOM 更快)。一些收集器(serial、cms 和 G1)具有广泛的调整选项,用于选择各个区域的大小,使您可以将时间浪费在徒劳地尝试解决问题上。

最终,真正的解决方案是相当不愉快的。首先,是安装更多内存。其次,是使用较小的阵列。三、就是用ByteBuffer.allocateDirect .直接字节缓冲区(及其 int/float/double 包装器)是类似数组的对象,具有类似数组的性能,分配在操作系统的 native 堆上。操作系统堆使用 CPU 的虚拟内存硬件,没有碎片问题,甚至可以有效地使用磁盘的交换空间(允许您分配比可用 RAM 更多的内存)。然而,一个很大的缺点是 JVM 并不真正知道什么时候应该释放直接缓冲区,这使得这个选项对于长期存在的对象更可取。最后的,可能是最好的,当然也是最不愉快的选择是使用 JNI 调用本地分配和释放内存,并通过将其包装在 ByteBuffer 中在 Java 中使用它。 .

关于java - 为什么当 20% 的堆空闲时我会得到 OutOfMemory?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17051126/

相关文章:

Java 垃圾回收和原子事件/停止 gc 暂停中断一系列函数调用

java - 如何在字符串中使用引号 "or ' 字符?

c# - 强制对数组进行垃圾回收,C#

java - 局部变量的垃圾收集

java - 在所有用户定义的方法中调用 System.gc() 是一种好习惯吗

Java 条件运算符速度与 Hotspot VM 版本

Java 静态类变量

java - ADF 文件导出不起作用。不通过调用exportAreaDescriptionFile() 也不通过Intent

Java Compareto方法初学者

java - 为什么 getSum 没有被热点 jvm 内联?