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

相关文章
|
2月前
|
存储 缓存 安全
Java内存模型深度解析:从理论到实践####
【10月更文挑战第21天】 本文深入探讨了Java内存模型(JMM)的核心概念与底层机制,通过剖析其设计原理、内存可见性问题及其解决方案,结合具体代码示例,帮助读者构建对JMM的全面理解。不同于传统的摘要概述,我们将直接以故事化手法引入,让读者在轻松的情境中领略JMM的精髓。 ####
43 6
|
1月前
|
安全 Java 程序员
深入理解Java内存模型与并发编程####
本文旨在探讨Java内存模型(JMM)的复杂性及其对并发编程的影响,不同于传统的摘要形式,本文将以一个实际案例为引子,逐步揭示JMM的核心概念,包括原子性、可见性、有序性,以及这些特性在多线程环境下的具体表现。通过对比分析不同并发工具类的应用,如synchronized、volatile关键字、Lock接口及其实现等,本文将展示如何在实践中有效利用JMM来设计高效且安全的并发程序。最后,还将简要介绍Java 8及更高版本中引入的新特性,如StampedLock,以及它们如何进一步优化多线程编程模型。 ####
31 0
|
2月前
|
存储 Java 编译器
Java内存模型(JMM)深度解析####
本文深入探讨了Java内存模型(JMM)的工作原理,旨在帮助开发者理解多线程环境下并发编程的挑战与解决方案。通过剖析JVM如何管理线程间的数据可见性、原子性和有序性问题,本文将揭示synchronized关键字背后的机制,并介绍volatile关键字和final关键字在保证变量同步与不可变性方面的作用。同时,文章还将讨论现代Java并发工具类如java.util.concurrent包中的核心组件,以及它们如何简化高效并发程序的设计。无论你是初学者还是有经验的开发者,本文都将为你提供宝贵的见解,助你在Java并发编程领域更进一步。 ####
|
2月前
|
存储 算法 Java
Java内存管理深度剖析与优化策略####
本文深入探讨了Java虚拟机(JVM)的内存管理机制,重点分析了堆内存的分配策略、垃圾回收算法以及如何通过调优提升应用性能。通过案例驱动的方式,揭示了常见内存泄漏的根源与解决策略,旨在为开发者提供实用的内存管理技巧,确保应用程序既高效又稳定地运行。 ####
|
3月前
|
缓存 easyexcel Java
Java EasyExcel 导出报内存溢出如何解决
大家好,我是V哥。使用EasyExcel进行大数据量导出时容易导致内存溢出,特别是在导出百万级别的数据时。以下是V哥整理的解决该问题的一些常见方法,包括分批写入、设置合适的JVM内存、减少数据对象的复杂性、关闭自动列宽设置、使用Stream导出以及选择合适的数据导出工具。此外,还介绍了使用Apache POI的SXSSFWorkbook实现百万级别数据量的导出案例,帮助大家更好地应对大数据导出的挑战。欢迎一起讨论!
305 1
|
1月前
|
存储 监控 算法
Java内存管理深度剖析:从垃圾收集到内存泄漏的全面指南####
本文深入探讨了Java虚拟机(JVM)中的内存管理机制,特别是垃圾收集(GC)的工作原理及其调优策略。不同于传统的摘要概述,本文将通过实际案例分析,揭示内存泄漏的根源与预防措施,为开发者提供实战中的优化建议,旨在帮助读者构建高效、稳定的Java应用。 ####
40 8
|
1月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
1月前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
58 5
|
1月前
|
存储 算法 Java
Java内存管理深度解析####
本文深入探讨了Java虚拟机(JVM)中的内存分配与垃圾回收机制,揭示了其高效管理内存的奥秘。文章首先概述了JVM内存模型,随后详细阐述了堆、栈、方法区等关键区域的作用及管理策略。在垃圾回收部分,重点介绍了标记-清除、复制算法、标记-整理等多种回收算法的工作原理及其适用场景,并通过实际案例分析了不同GC策略对应用性能的影响。对于开发者而言,理解这些原理有助于编写出更加高效、稳定的Java应用程序。 ####
|
1月前
|
安全 Java 程序员
Java内存模型的深入理解与实践
本文旨在深入探讨Java内存模型(JMM)的核心概念,包括原子性、可见性和有序性,并通过实例代码分析这些特性在实际编程中的应用。我们将从理论到实践,逐步揭示JMM在多线程编程中的重要性和复杂性,帮助读者构建更加健壮的并发程序。

热门文章

最新文章