8 种 Java- 内存溢出之二 -GC overhead limit exceeded

简介: 8 种 Java- 内存溢出之二 -GC overhead limit exceeded

2.1 GC overhead limit exceeded 概述

Java 运行时环境包含一个内建的垃圾收集线程. 在很多其他编程语言中, 开发者需要手动分配和释放内存区域, 以保证释放的内存可以被复用.

但是 Java 应用只需要分配内存. 只要一个特定的内存空间不再使用, 一个单独的叫做 垃圾收集 的线程会清理内存. GC 如何检测特定内存不再使用的内容超出本文范围, 但是你可以信任 GC 可以把这个活做的很好.

java.lang.OutOfMemoryError: GC overhead limit exceeded(GC 开销超过限制)错误意味着 GC 尝试释放内存但是却无法完成任何一件事情. 默认它发生在: JVM 在 GC 中花费超过 98% 的时间,GC 之后, 只有不到 2% 的堆被释放.

java.lang.OutOfMemoryError: GC overhead limit exceeded错误在以下场景会出现: 你的应用消耗了相当多的可用内存, GC 反复尝试清理, 但均以失败告终.

2.2 原因

java.lang.OutOfMemoryError: GC overhead limit exceeded错误就是 JVM 在发信号告诉你: 应用消耗了太多时间在做垃圾收集, 而且还收效甚微. 默认情况的配置是 JVM 会在以下情况下抛出该错误: JVM 在 GC 中花费超过 98% 的时间,GC 之后, 只有不到 2% 的堆被释放.

如果这个 GC 开销限制不存在,会发生什么情况? 需要注意的是 java.lang.OutOfMemoryError: GC overhead limit exceeded 错误只有当在几次 GC 周期之后, 只有 2% 的内存被释放的情况下才会抛出. 这意味着只有很少量的内存 GC 能够清理, 而且还会再次被迅速填满, 导致 GC 再次开始清理线程. 这形成了一个恶性循环,CPU 100% 在忙于 GC,没有实际工作可做. 应用最终用户面临着极端响应慢的情况 – 本来几毫秒就能完成的操作需要花费几分钟来完成.

所以, java.lang.OutOfMemoryError: GC overhead limit exceeded消息是一个实现 快速失败 (fail-fast) 原则的相当好的案例.

2.3 案例

在下列案例中, 我们通过初始化一个 Map 并且在一个无限循环里增加一个 key-value 对到该 Map 来创建一个 GC overhead limit exceeded 错误:

class Wrapper {
    public static void main(String args[]) throws Exception {
        Map map = System.getProperties();
        Random r = new Random();
        while (true) {
            map.put(r.nextInt(), "value");
        }
    }
}
JAVA

正如你可能猜到的那样,这是不可能结束的. 事实上, 当我们使用以下语句运行:

java -Xmx100m -XX:+UseParallelGC Wrapper

我们不久就会看到 java.lang.OutOfMemoryError: GC overhead limit exceeded 信息. 但是上边的例子很棘手. 当选择不同的 Java heap size 或者不同的 GC 策略, 在 Mac OS X 10.9.2, Hotspot 1.7.0_45 上死法各不相同. 例如, 当我用更小的 Java heap size 运行:

java -Xmx10m -XX:+UseParallelGC Wrapper

该应用会抛出一个更常见的 java.lang.OutOfMemoryError: Java heap space 消息. 当我使用不同的 GC 策略运行它, 比如-XX:+UseConcMarkSweepGC-XX:+UseG1GC, 错误被默认的异常 handler 捕获, 并且在这种情况下,因为堆耗尽, 堆栈跟踪甚至不能在异常创建中被填充。

这些变化确实是很好的例子,说明在资源受限的情况下你不能预测你的应用程序将会以怎样的方式死亡,所以不要以你的期望为基础寄托在要完成的具体操作序列上。

2.4 解决办法

作为一个半开玩笑的解决方案, 如果你希望避免 java.lang.OutOfMemoryError: GC overhead limit exceeded 消息, 可以在启动脚本里增加下列信息:

-XX:-UseGCOverheadLimit

强烈建议不要使用这个参数 – 这只是饮鸩止渴: 最终应用会耗尽内存, 需要修复. 指定这个参数只是用一个更常见的消息java.lang.OutOfMemoryError: Java heap space 掩盖了 java.lang.OutOfMemoryError: GC overhead limit exceeded 错误.

另一种方式(临时的), 是给 JVM 进程更多的内存. 再次说明, 加这个很简单,但是最终仍会导致错误:

java.lang.OutOfMemoryError: Java heap space

在上边例子中, 给 Java 进程 1GB 的 heap. 增加这个值会解决 GC 开销限制问题如果你的应用先是出现内存不足. 但是如果你希望确保你已经解决了潜在的问题而不是掩盖 java.lang.OutOfMemoryError: GC overhead limit exceeded 症状, 你不应该仅止于此. 对于这种情况, 联系我就是最好的方式(@ ̄ー ̄@). 当然你手头上也有不同的工具可供选择, 如: profilers 和内存 dump 分析工具. 但要准备好投入大量的时间, 而且要意识到这一点: 工具会对你的 Java 运行时造成了巨大的开销,因此它们不适合生产环境使用。

对于这种问题, 其实 Dynatrace 也会进行告警. 我这有一篇分析的案例供参考.

相关文章
|
1月前
|
缓存 easyexcel Java
Java EasyExcel 导出报内存溢出如何解决
大家好,我是V哥。使用EasyExcel进行大数据量导出时容易导致内存溢出,特别是在导出百万级别的数据时。以下是V哥整理的解决该问题的一些常见方法,包括分批写入、设置合适的JVM内存、减少数据对象的复杂性、关闭自动列宽设置、使用Stream导出以及选择合适的数据导出工具。此外,还介绍了使用Apache POI的SXSSFWorkbook实现百万级别数据量的导出案例,帮助大家更好地应对大数据导出的挑战。欢迎一起讨论!
164 1
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
68 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
监控 Java Linux
Java 性能调优:调整 GC 线程以获得最佳结果
Java 性能调优:调整 GC 线程以获得最佳结果
74 11
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制(GC)
本文将探讨Java的自动内存管理核心——垃圾回收机制。通过详细解析标记-清除算法、复制算法和标记-整理算法等常用垃圾回收算法,以及CMS、G1等常见垃圾回收器,帮助读者更好地理解Java应用的性能优化和内存管理。同时,探讨分代收集、分区收集等策略在实际项目中的应用。结语部分总结了垃圾回收机制在Java开发中的重要性,并展望了未来可能的发展。
58 0
|
4月前
|
算法 Java 开发者
Java面试题:Java内存探秘与多线程并发实战,Java内存模型及分区:理解Java堆、栈、方法区等内存区域的作用,垃圾收集机制:掌握常见的垃圾收集算法及其优缺点
Java面试题:Java内存探秘与多线程并发实战,Java内存模型及分区:理解Java堆、栈、方法区等内存区域的作用,垃圾收集机制:掌握常见的垃圾收集算法及其优缺点
39 0
|
算法 Java
【Java 虚拟机原理】垃圾回收算法 ( Java 虚拟机内存分区 | 垃圾回收机制 | 引用计数器算法 | 引用计数循环引用弊端 )
【Java 虚拟机原理】垃圾回收算法 ( Java 虚拟机内存分区 | 垃圾回收机制 | 引用计数器算法 | 引用计数循环引用弊端 )
143 0
|
存储 Java 程序员
java-jvm-内存分区
 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆、栈以及静态数据区。那么在Java语言当中,内存又是如何划分的呢?   由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。
1431 0
|
8天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
下一篇
无影云桌面