1、查看内存信息,对象实例数、对象占有大小
jmap -histo 进程号>./log.txt
2、查看堆的配置信息和使用情况
jmap - heap 进程号
3、将堆的快照信息dump下来,使用java自带的jvisualvm.exe打开分析
jmap -dump:format=b,file=dump.hprof 进程号
查看线程信息,死锁时用到可以查看线程阻塞信息
jstack -l 进程号 > jstack.log
查看gc日志
jstat -gc 进程号
持续打印gc日志
jstat -gc 进程号 1000 10
1、jvisualvm.exe可以开启远程连接,但生产一般不用。只能在测试服务器上进行压测的时候可以开启。开启需要在服务器上加命令:自行百度
2、记录下outofmemory时的dump文件
-Xmx10M -Xms10M
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=D:/dump.dump
dump下来的文件使用java自带的jvisualvm.exe打开分析
目的:让朝生夕死的对象在年轻代就干掉,不要给他到达老年代
案例
线上系统频繁发生full gc。有卡顿,但是线上的系统压力也不是很大。
1、通过jstat -gc 进程号 查看gc情况
机器和GC情况
JVM的参数设置如下
-Xms1536M -Xmx1536M -Xmn512M -Xss256K -XX :SurvivorRatio=6 -XX:MetaspaceSize=256M
-XX:+UseParNewGC -xx:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
计算各区大小
jvm总内存1536M
新生代堆内存-Xmn512M,SurvivorRatio=6根据比例得知:伊甸园区:幸存1区:幸存2区 = 6:1:1。伊甸园区 384M,幸存1区 64M,幸存2区 64M(幸存1区与二区内存相同,是否是,进行youngGC的时候,将一区挪到二区,或从二区挪到一区)
老年区 = 堆大小1536-新生代堆521 = 1G
条件:youngGC 7天1万次,大概每隔1分钟发生一次youngGC,一分钟占满伊甸园区384M,每秒会产生6M对象
fullGC 大概每20分钟一次,因为XX:CMSInitiatingOccupancyFraction设置的75%。所有每20分钟大概有700M的对象移入老年代
验证调整
分析为什么会频繁触发fullGC
youngGC 7天1万次
平均一分钟一次youngGC
也就是每60秒会占满伊甸园区384M
youngGC 在10秒到60秒一次算是比较正常的。而且每次youngGC完只剩下几十MB进入到幸存区,只是偶尔会有一两次幸存区放不下才会进到老年代。
这里youngGC并不频繁,但是FULLGC却那么频繁有点反常
猜测触发FULLGC很可能是有大对象或者触发空间担保机制进入到了老年代
大对象是因为产生的对象伊甸园区放不下了,直接就放到了老年代
这个时候我们通过jstat运行的时候就观察到一个现象,就是老年代里的内存占用在系统运行的时候,不知道为什么系统运行着运行着就会突然有几百MB的对象占据在里面,大概有五六百MB的对象,一直占据在老年代中。那只能是有大对象生成
分析到这里就很简单了,就先用jstat命令观察老年代突然进入几百兆对象。然后将内存dump下来。Visual VM之类的可视化工具来分析dump内存快照
直接定位出来那个几百MB的大对象,就是几个Map之类的数据结构。根据业务排查,有SQL会在特定场景下select * from tbl,后面的where条件都没有用上,导致查询出大量数据,从而这些对象都进入到老年代,引发fullGC
参考:https://www.jianshu.com/p/b6d2aa15c2a8