线上机器频繁FullGC 怎么办?
对于这个问题一般来说有以下多种原因。
- 大对象:系统一次性加载了很多数据到内存(比如SQL未分页),导致大对象进入老年代
- 内存泄露:频繁创建大量对象,但无法回收(比如IO对象使用后未调用close释放资源),导致OOM。
- 程序频繁声场长生命周期对象,带这些对象存活年龄超过分代年龄后,进入老年代,引发GC。
- 显示调用了GC方法,包括自己代码和框架的代码
- JVM参数设值问题
对于以上几种问题,首先我们上来就先来一句shell。
//PID代表当前问题进程的id,1000表示1秒刷新显示一次。
jstat -gc PID 1000
这样可以查看GC次数,时间等信息,主要关注几个参数, OC OU FGC FGCT。OC 老年代总容量,OU老年代已用容量,FGC老年代耗时,FGCT是回收次数。
这样就好懂了如果OU在几次FullGC没有变小的话,就代表老年代没有回收成功,然后祭出第二句。
//查看堆内存存活对象,并按照空间排序
jmap -histo pid | head -n20
我找了个图,类似于这样,可以知道占用堆内存空间最多的对象就是第一个,[B,也就最大的是一个Byte数组,如果是日常业务开发中,那么就可以知道,是什么类型的对象占用了内存,就大概知道是哪里出现了问题。
如果这样都还是不知道,就献祭出最后大招,dump东西下来,相比你也听过dump,但是是不是不知道大佬们说的是啥?我和你解释下就是可以看到堆内应用的一些类似于更详细的信息。
jmap -dump:live,file=xxxxxxxxx/aaaaa.hprof pid
live表示存活的对象,xxxxxxx表示你想导出dump文件的地址,如果是mac的话想知道当前文件的地址是什么可以使用pwd命令,然后粘贴最后将这个dump文件导入到Java VisualVM里面,就可以查看,如下图所示,主要的目的就是定位错误在什么地方,是什么地方的对象导致内存过高。记住核心,然后去找问题。