各命令的使用
JMAP
1、查看内存信息,对象实例数、对象占有大小
jmap -histo 进程号>./log.txt
2、查看堆的配置信息和使用情况
jmap - heap 进程号
3、将堆的快照信息dump下来,使用java自带的jvisualvm.exe打开分析
jmap -dump:format=b,file=dump.hprof 进程号
Jstack
查看线程信息,死锁时用到可以查看线程阻塞信息
jstack -l 进程号 > jstack.log
jstat
查看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打开分析
实战例子
cpu飙高定位问题
- top 查看占用较大的进程号
- 使用 top -p 进程,再按大写的H 查看该进程下对应的线程情况
- 找到占有最大的线程id
- jstack -l 进程号
- 用jstack打出来的线程nid和top -p 查到的线程进行对比。就能得到对应的代码信息
- 注意jstack 里面的线程信息 nid是16进制的,top里面找到的线程信息需要转换一下
优化full gc
目的:让朝生夕死的对象在年轻代就干掉,不要给他到达老年代
案例
线上系统频繁发生full gc。有卡顿,但是线上的系统压力也不是很大。
1、通过jstat -gc 进程号 查看gc情况
机器和GC情况
- 机器配置:2核4G
- JVM内存大小:2G
- 系统运行时间:7天
- 期间发生的Full GC次数和耗时:500多次,200多秒
- 期间发生的Young GC 次数和耗时:1万多次,500多秒
- 大致算下来每天会发生70多次Full GC,平均每小时3次,每次Full GC在400毫秒左右;每天会发生1000多次Young GC, 每分钟会发生一次,每次Young GC 在50毫秒左右
- 上述数据对任何一个线上系统,用jstat -gc 进程号,可以轻松看出来,因为jstat显示出来的Full GC和Young GC的次数都是系统启动以来的总次数。可回看上方jstat -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