万隆的笔记 万隆的笔记
博文索引
笔试面试
  • 在线学站

    • 菜鸟教程 (opens new window)
    • 入门教程 (opens new window)
    • Coursera (opens new window)
  • 在线文档

    • w3school (opens new window)
    • Bootstrap (opens new window)
    • Vue (opens new window)
    • 阿里开发者藏经阁 (opens new window)
  • 在线工具

    • tool 工具集 (opens new window)
    • bejson 工具集 (opens new window)
    • 文档转换 (opens new window)
  • 更多在线资源
  • Changlog
  • Aboutme
GitHub (opens new window)
博文索引
笔试面试
  • 在线学站

    • 菜鸟教程 (opens new window)
    • 入门教程 (opens new window)
    • Coursera (opens new window)
  • 在线文档

    • w3school (opens new window)
    • Bootstrap (opens new window)
    • Vue (opens new window)
    • 阿里开发者藏经阁 (opens new window)
  • 在线工具

    • tool 工具集 (opens new window)
    • bejson 工具集 (opens new window)
    • 文档转换 (opens new window)
  • 更多在线资源
  • Changlog
  • Aboutme
GitHub (opens new window)
  • 大纲

  • 走近Java

  • 内存与垃圾回收

  • 字节码与类加载

  • 性能监控与调优

  • 监控与性能调优案例

    • JVM监控与性能调优案例概述
    • Tomcat堆溢出分析
    • 堆溢出
    • 方法区溢出
    • GC overhead limit exceeded
      • 报错信息
      • 案例模拟
      • 代码解析
      • 分析与解决
    • 线程溢出
    • 调整堆大小提高服务的吞吐量
    • JVM优化之JIT优化
    • 合理配置堆内存
    • CPU占用很高排查方案
    • G1并发执行的线程数对性能的影响
    • 调整垃圾回收器提高服务的吞吐量
    • 日均百万级订单交易系统如何设置JVM参数
    • 内存泄漏与内存溢出
  • Java虚拟机
  • 监控与性能调优案例
2022-03-18
目录

GC overhead limit exceeded

# GC overhead limit exceeded

# 报错信息

java.lang.OutOfMemoryError: GC overhead limit exceeded

  • 这个是JDK6新加的错误类型,一般都是堆太小导致的。 Sun 官方对此的定义:超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常。 本质是一个预判性的异常 ,抛出该异常时系统没有真正的内存溢出

# 案例模拟

示例代码1

public static void test1() {
    int i = 0;
    List<String> list = new ArrayList<>();
    try {
        while (true) {
            list.add(UUID.randomUUID().toString().intern());
            i++;
        }
    } catch (Throwable e) {
        System.out.println("************i: " + i);
        e.printStackTrace();
        throw e;
    }
}


示例代码1JVM参数配置:

-Xms10M
-Xmx10M
-XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/dumpExceeded.hprof
-XX:+PrintGCDateStamps
-Xloggc:log/gc-oomExceeded.log

示例代码1运行结果:

java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.util.Arrays.copyOf(Arrays.java:3332)
	at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
	at java.lang.StringBuilder.append(StringBuilder.java:136)
	at java.util.UUID.toString(UUID.java:380)
  ......

示例代码2

public static void test2() {
    String str = "";
    Integer i = 1;
    try {
        while (true) {
            i++;
            str += UUID.randomUUID();
        }
    } catch (Throwable e) {
        System.out.println("************i: " + i);
        e.printStackTrace();
        throw e;
    }
}

示例代码2JVM参数配置:

-Xms10M
-Xmx10M
-XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=heap/dumpHeap1.hprof
-XX:+PrintGCDateStamps
-Xloggc:log/gc-oomHeap1.log

示例代码2运行结果:

java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3332)
	at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
	at java.lang.StringBuilder.append(StringBuilder.java:136)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
  ······

# 代码解析

代码1:运行期间将内容放入常量池的典型案例。
intern()方法 :
- 如果字符串常量池里面已经包含了等于字符串X的字符串,那么就返回常量池中这个字符串的引用; 
- 如果常量池中不存在,那么就会把当前字符串添加到常量池并返回这个字符串的引用 

代码2:不停的追加字符串str 。

你可能会疑惑,看似demo也没有差太多,为什么第二个没有报GC overhead limit exceeded呢?以上两个demo的区别在于:

  • Java heap space的demo每次都能回收大部分的对象(中间产生的UUID),只不过有一个对象是无法回收的,慢慢长大,直到内存溢出
  • 而GC overhead limit exceeded的demo由于每个字符串都在被list引用,所以无法回收,很快就用完内存,触发不断回收的机制。

# 分析与解决

示例1日志解析:

2022-03-16T21:21:51.670-0800: 1.534: [Full GC (Ergonomics) [PSYoungGen: 2048K->2043K(2560K)] [ParOldGen: 7139K->7139K(7168K)] 9187K->9183K(9728K), [Metaspace: 3672K->3672K(1056768K)], 0.0227713 secs] [Times: user=0.11 sys=0.01, real=0.02 secs] 
2022-03-16T21:21:51.693-0800: 1.557: [Full GC (Ergonomics) [PSYoungGen: 2048K->2044K(2560K)] [ParOldGen: 7139K->7139K(7168K)] 9187K->9183K(9728K), [Metaspace: 3672K->3672K(1056768K)], 0.0191276 secs] [Times: user=0.12 sys=0.00, real=0.02 secs] 
2022-03-16T21:21:51.713-0800: 1.576: [Full GC (Ergonomics) [PSYoungGen: 2048K->0K(2560K)] [ParOldGen: 7139K->527K(7168K)] 9187K->527K(9728K), [Metaspace: 3676K->3676K(1056768K)], 0.0061429 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 

通过查看GC日志可以发现,系统在频繁性的做FULL GC,但是却没有回收掉多少空间,那么引起的原因可能是因为内存不足,也可能是存在内存泄漏的情况,接下来我们要根据堆dump文件来具体分析。

VisualVM分析dump,定位代码块:

oom_case3_1

MAT分析dump,定位代码块:

oom_case3_2

oom_case3_3

oom_case3_4

解决方法:

  1. 检查项目中是否有大量的死循环或有使用大内存的代码,优化代码。
  2. 添加参数 -XX:-UseGCOverheadLimit 禁用这个检查,其实这个参数解决不了内存问题,只是把错误的信息延后,最终出现 java.lang.OutOfMemoryError: Java heap space。
  3. dump内存,检查是否存在内存泄漏,如果没有,加大内存。
#JVM监控与性能调优案例
上次更新: 5/28/2023, 10:57:53 PM
线程溢出

线程溢出→

最近更新
01
2025
01-15
02
Elasticsearch面试题
07-17
03
Elasticsearch进阶
07-16
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式