01-频繁Full-GC问题怎么排查
2025年1月20日大约 4 分钟
你遇到过Full-GC的问题么?Full-GC可能由哪些问题导致的?你的排查思路是什么?
为什么会FUll-GC?
当Java应用程序运行时,JVM会为对象分配内存。如果堆内存不足以容纳新创建的对象,JVM将触发垃圾回收(GC)以尝试释放更多空间。如果GC无法回收足够的空间,最终会导致Full GC。
堆内存不足可能是由于内存泄漏、大对象频繁分配或不合理的堆大小配置引起的。
一般来说,如果是线上项目的排查一般不会考虑不合理的堆大小配置这个选项,这种可能一般出现在新项目启动上才会出现配置问题,活着项目出现了过大的改动导致需要更多内存启动和运行。
排查方法
启用详细的GC日志:通过
-XX:+PrintGCDetails
和-XX:+PrintGCDateStamps
参数记录详细的GC信息,查看每次GC的时间戳和回收情况。监控堆内存使用情况:
- 使用
jstat -gc <pid>
命令定期检查堆内存的使用情况。 - 使用
VisualVM
连接到JVM,实时监控堆内存的变化。 - 使用
jmap -heap <pid>
获取当前堆内存的快照。
- 使用
**
jstat:
**用于监控JVM的垃圾收集统计信息:jstat -gc <pid>
**
jmap:
**用于生成堆转储文件(Heap Dump),帮助分析内存使用情况。jmap -dump:live,format=b,file=heap.hprof <pid>
**VisualVM:**图形化工具,提供丰富的监控和分析功能。
具体来说,一般的操作是利用jmap
的命令把dump文件下载到本地,在通过VisualVM等可视化工具来分析对象的引用链。
初步分析GC日志文件
通过查看GC日志,初步判断问题类型:
- Full GC频率:观察Full GC的频率和每次GC的时间。
- 堆内存使用情况:关注年轻代(Young Generation)和老年代(Old Generation)的内存使用情况。
- 晋升失败(Promotion Failure):检查是否有晋升失败的情况。
- 永久代/元空间使用情况:关注永久代或元空间的使用率。
问题特征
具体问题 | 特征 |
---|---|
老年代内存不足 | GC日志中频繁出现“Full GC”,伴随老年代内存接近上限。 晋升失败(Promotion Failure):GC日志中显示晋升失败的信息。 老年代内存增长迅速:通过 jstat -gcold <pid> 命令监控老年代内存使用情况,发现老年代内存快速增长。 |
永久代/元空间不足 | GC日志中频繁出现“Metaspace GC” 或者 “PermGen GC” 永久代/元空间接近上限:通过 jstat -gcpermcapacity <pid> 或jcmd <pid> VM.flags 查看永久代或元空间容量。类加载频繁:应用动态加载大量类,或者类加载器未正确释放不再使用的类。 |
大对象分配 | Heap Dump分析中存在大量大对象:通过Eclipse MAT 或VisualVM 分析Heap Dump文件,发现存在大量大对象。直接进入老年代的大对象:GC日志中显示大对象直接分配到老年代。 |
解决方案
老年代内存不足
- 增加老年代内存:调整
-XX:NewRatio
参数,适当增大老年代的比例。 - 减少大对象分配:优化代码,避免频繁创建大对象。
永久代/元空间不足
- 增加永久代/元空间:调整
-XX:MaxPermSize
(Java 7及之前)或-XX:MaxMetaspaceSize
(Java 8及之后)参数。 - 优化类加载:确保类加载器正确释放不再使用的类,减少动态加载类的数量。
大对象分配
- 优化对象分配:尽量减少大对象的创建,考虑使用对象池或分批处理。
- 调整TENURING_THRESHOLD:适当调整对象晋升到老年代的阈值,减少大对象直接进入老年代的机会。
思考
其实在线上排查的问题时候,我会首先考虑大内存分配的原因,这也是bad code 最容易出现问题的地方,其他的两个出现的问题都很少。