likes
comments
collection
share

java.lang.OutOfMemoryError: GC overhead limit exceeded的一次分析

作者站长头像
站长
· 阅读数 5

java环境 java version "13.0.2" 2020-01-14 Java(TM) SE Runtime Environment (build 13.0.2+8) Java HotSpot(TM) 64-Bit Server VM (build 13.0.2+8, mixed mode, sharing)

一.GC overhead limit exceeded是什么问题

官方原话:JVM花费了98%的时间进行垃圾回收,而只得到2%可用的内存,频繁的进行内存回收(最起码已经进行了5次连续的垃圾回收),JVM就会曝出java.lang.OutOfMemoryError: GC overhead limit exceeded错误。其实就是内存泄漏/数据量激增,GC想回收内存发现哪儿哪儿的对象都是被引用着,连续GC都无法有效回收内存就抛出该异常。

二.通过dump文件分析原因

1.什么是dump文件

dump文件是一个进程或者系统在某一个时间段的快照(镜像)。包含了程序运行的模块信息、线程信息、堆栈调用信息、异常信息等数据。

2.如何获取dump文件

  1. 使用jmap命令
jmap -dump:format=b,file=user.dump [pid]
  1. IDEA本地使用profiler创建

java.lang.OutOfMemoryError: GC overhead limit exceeded的一次分析

  1. 启动参数配置(常用)
-XX:+HeapDumpOnOutOfMemoryError  
-XX:HeapDumpPath=/home/logs/

3.通过IDEA的Profiler分析dump文件

官网使用具体详情 Analyze the memory snapshot

参数意义
count实例数
Shallow是指在内存中分配给一个对象本身的空间大小,而不包括它引用的其他对象或占用的额外空间。
Retained"Retained size"(保留大小)是指一个对象及其所有可达对象在内存中所占用的总空间大小。换句话说,它是通过计算从根对象(如全局变量、活动线程等)到该对象的引用链,确定该对象及其引用对象的总体内存占用。与 "Shallow Size"(浅层大小)不同,"Retained size" 不仅包括对象本身的大小,还包括对象引用的其他对象所占用的空间。保留的大小是可以通过垃圾收集该对象来回收的内存量。
Biggest Objects选项卡按保留内存的大小列出了占用内存大的对象。并显示到保留它们的引用对象的包路径
GC RootGC Root 是起始点,是一组被认为是活动的对象,它们将决定哪些对象在内存中保留,哪些对象被清理掉。类似于一棵树的根部,它们与其他对象之间的引用链构成了整个内存中对象的连接关系。GC Roots选项卡显示了具有相应垃圾收集器根对象的类列表。该信息是在快照拍摄时无法垃圾收集的所有对象的概述
Merged Paths选项卡按类分组,并显示到保留引用它们的路径。
Summary显示常规信息,例如,线程的总大小、实例数量和堆栈跟踪
Packages根据对象所属的包名进行分类和统计,能快速定位到具体包的问题

java.lang.OutOfMemoryError: GC overhead limit exceeded的一次分析

java.lang.OutOfMemoryError: GC overhead limit exceeded的一次分析

java.lang.OutOfMemoryError: GC overhead limit exceeded的一次分析

java.lang.OutOfMemoryError: GC overhead limit exceeded的一次分析

这种调用链比较简单的,从Biggest Objects和GC Roots都能明显看出,主要的原因是mybatis在做结果集封装时,发生了内存泄漏,此时封装对象数约为61w,占用内存约为1282M

三.GC日志分析

2023-11-23T11:28:12.075+0800: 1.110: [GC (Allocation Failure) [PSYoungGen: 261120K->12707K(304640K)] 261120K->12795K(1000960K), 0.0064442 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
2023-11-23T11:28:12.170+0800: 1.204: [GC (Metadata GC Threshold) [PSYoungGen: 77809K->9700K(304640K)] 77897K->9796K(1000960K), 0.0047312 secs] [Times: user=0.00 sys=0.02, real=0.00 secs] 
2023-11-23T11:30:49.861+0800: 158.895: [Full GC (Ergonomics) [PSYoungGen: 231905K->68502K(464384K)] [ParOldGen: 1231152K->1392558K(1392640K)] 1463057K->1461061K(1857024K), [Metaspace: 82928K->82928K(1126400K)], 4.7323658 secs] [Times: user=8.44 sys=0.19, real=4.73 secs] 

参数意义
2023-11-23T11:28:12.075+0800: 1.110日志打印时间,-XX:+PrintGCDateStamps:新增此配置便可打印出来
GC (Allocation Failure)发生了一次Minor GC垃圾回收,它不区分新生代GC还是老年代GC,括号里的内容是GC发生的原因,这里的Allocation Failure的原因是新生代中没有足够区域能够存放需要分配的数据而失效
[PSYoungGen: 261120K->12707K(304640K)]PSYoungGen:表示GC发生的区域在年轻代。对应的有ParOldGen老年代区域
261120K->12795K(1000960K)GC前该堆内存使用容量 -> GC后堆内存使用容量(堆内存总容量)
0.0064442 secs整个GC花费的时间,单位是秒
[Times: user=0.00 sys=0.00, real=0.01 secs]user:进程执行用户态代码(核心之外)所使用的时间,这是执行此进程所使用的实际CPU时间,其他进程和此进程阻塞的时间并不包括在内。在垃圾收集的情况下,表示GC线程执行所使用的CPU总时间 sys:进程在内核态消耗的CPU时间,即在内核执行系统调用或等待系统事件所使用的CPU时间 real:程序从开始到结束所用的时钟时间。这个时间包括其他进程使用的时间片和进程阻塞的时间(比如等待IO完成)。对于并行gc,这个数字应该接近(用户时间+系统时间)/垃圾收集器使用的线程数
Full GC (Ergonomics)Full GC,它不区分新生代GC还是老年代GC括号里的内容是gc发生的原因,Ergonomics策略是一种自适应垃圾回收策略 GCFull GC(System):调用了System.gc()方法 Metadata GC Threshold:Metaspace区不够用了

四、在线分析网站

GC日志在线分析 dump文件在线分析