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

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命令
lua 复制代码
jmap -dump:format=b,file=user.dump [pid]
  1. IDEA本地使用profiler创建
  1. 启动参数配置(常用)
ruby 复制代码
-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 Root GC Root 是起始点,是一组被认为是活动的对象,它们将决定哪些对象在内存中保留,哪些对象被清理掉。类似于一棵树的根部,它们与其他对象之间的引用链构成了整个内存中对象的连接关系。GC Roots选项卡显示了具有相应垃圾收集器根对象的类列表。该信息是在快照拍摄时无法垃圾收集的所有对象的概述
Merged Paths 选项卡按类分组,并显示到保留引用它们的路径。
Summary 显示常规信息,例如,线程的总大小、实例数量和堆栈跟踪
Packages 根据对象所属的包名进行分类和统计,能快速定位到具体包的问题

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

三.GC日志分析

log 复制代码
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文件在线分析

相关推荐
java_upp36 分钟前
JVM内存溢出和内存泄漏的区别及排查方法
jvm·内存泄漏·内存溢出
IT_陈寒1 小时前
Python线程池吞了异常还不告诉我,这谁顶得住啊
前端·人工智能·后端
小强库计算机毕业设计1 小时前
SpringBoot+Vue3 学生宿舍管理系统实战
java·spring boot·后端·vue·学生宿舍管理系统
小狼1831 小时前
实践6|SDD 实战:AI 写的规格被推翻了四次
后端·claude
篮框坏了1 小时前
"DeepSeek Harness 实测:一毛钱干三活,但默认配置是个坑"
后端·ai编程
李冰然1 小时前
深入Java集合框架:HashMap源码解析(JDK 8)
java
jobBridge211 小时前
大模型到底是怎么"想"的?我把 Transformer 拆开,发现它其实是个"接词狂魔"
人工智能·后端·编程语言
人道领域2 小时前
【0-1的agent进阶篇】RAG:从Embedding到检索增强生成的底层逻辑
java·embedding·agent·rag
唐青枫2 小时前
看懂内存地址之后,才算真正入门 Zig:指针、切片与实战
后端
朋克洛德的码农2 小时前
Go并发-sync包四剑客:Mutex、RWMutex、WaitGroup、Once-从入门到原理
开发语言·后端·golang