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 也会进行告警. 我这有一篇分析的案例供参考.

相关文章
|
缓存 easyexcel Java
Java EasyExcel 导出报内存溢出如何解决
大家好,我是V哥。使用EasyExcel进行大数据量导出时容易导致内存溢出,特别是在导出百万级别的数据时。以下是V哥整理的解决该问题的一些常见方法,包括分批写入、设置合适的JVM内存、减少数据对象的复杂性、关闭自动列宽设置、使用Stream导出以及选择合适的数据导出工具。此外,还介绍了使用Apache POI的SXSSFWorkbook实现百万级别数据量的导出案例,帮助大家更好地应对大数据导出的挑战。欢迎一起讨论!
2403 1
|
Java 数据库
【YashanDB知识库】kettle同步大表提示java内存溢出
在数据导入导出场景中,使用Kettle进行大表数据同步时出现“ERROR:could not create the java virtual machine!”问题,原因为Java内存溢出。解决方法包括:1) 编辑Spoon.bat增大JVM堆内存至2GB;2) 优化Kettle转换流程,如调整批量大小、精简步骤;3) 合理设置并行线程数(PARALLELISM参数)。此问题影响所有版本,需根据实际需求调整相关参数以避免内存不足。
|
Java Shell 数据库
【YashanDB 知识库】kettle 同步大表提示 java 内存溢出
【问题分类】数据导入导出 【关键字】数据同步,kettle,数据迁移,java 内存溢出 【问题描述】kettle 同步大表提示 ERROR:could not create the java virtual machine! 【问题原因分析】java 内存溢出 【解决/规避方法】 ①增加 JVM 的堆内存大小。编辑 Spoon.bat,增加堆大小到 2GB,如: if "%PENTAHO_DI_JAVA_OPTIONS%"=="" set PENTAHO_DI_JAVA_OPTIONS="-Xms512m" "-Xmx512m" "-XX:MaxPermSize=256m" "-
|
监控 算法 Java
深入理解Java中的垃圾回收机制(GC)
本文将探讨Java的自动内存管理核心——垃圾回收机制。通过详细解析标记-清除算法、复制算法和标记-整理算法等常用垃圾回收算法,以及CMS、G1等常见垃圾回收器,帮助读者更好地理解Java应用的性能优化和内存管理。同时,探讨分代收集、分区收集等策略在实际项目中的应用。结语部分总结了垃圾回收机制在Java开发中的重要性,并展望了未来可能的发展。
538 27
|
监控 Java Linux
Java 性能调优:调整 GC 线程以获得最佳结果
Java 性能调优:调整 GC 线程以获得最佳结果
516 11
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
277 2
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
监控 算法 Java
Java面试题:如何在Java中触发一次Full GC?请详细解释垃圾回收机制和知识
Java面试题:如何在Java中触发一次Full GC?请详细解释垃圾回收机制和知识
1046 4
|
存储 算法 Java
通过图文给你讲明白java GC的垃圾回收机制
通过图文给你讲明白java GC的垃圾回收机制
通过图文给你讲明白java GC的垃圾回收机制
|
存储 监控 算法
JAVA GC垃圾回收机制详解
JAVA GC垃圾回收机制详解
517 0
JAVA GC垃圾回收机制详解

热门文章

最新文章