JVM排查定位
源自笔者某次回去等通知的故事
1. jps
列出正在运行的虚拟机进程、及其pid,命令参数有:
-l:输出主类全限定类名
-v:虚拟机进程启动时的JVM参数
jps -l
2. jstat
监视虚拟机各种运行状态,命令参数有:
-gc:监视堆状况
-gcutil:与-gc一致,不同于显示百分比
jstat -gcutil pid 时间间隔 查询次数
jstat -gcutil 11564 250 20
Surviro from Eden Old MeteSpace CSS YGC总次数 YGC总花费时间 FGC总次数 FGC总时间 GCT垃圾回收总时间
3. jinfo
实时产看和调整虚拟机各项参数,参数有:
-flag [ +-name / name=value ] 来运行时修改参数
jinfo pid
jinfo 11564
4. jmap
生成堆转储快照(headdump),或者 设置参数 -XX:+HeadDumpOnOutOfMemoryError参数,溢出时自动生成快照文件,文件中可以获取到:
- 对象信息:类、成员变量、直接量以及引用值
- 类信息:类加载器、名称、超类、静态成员
- Garbage Collections Roots:JVM可达的对象
- 线程栈以及本地变量:获取快照时的线程栈信息,以及局部变量的详细信息
其参数有:
-dump:生成Java堆转储文件,然后用VisualVM来打开
jmap -dump:format=b,file=filename pid
jmap -dump:format=b,file=C:\Users\Howl\Desktop\2020-6-3-heapdump.hprof 11564
-histo:查看堆中对象详细信息,包括类,实例数量,合计容量
jmap -histo pid
jmap -histo 11564
- 可以定位哪个类溢出
5. jsatck
生成当前线程存储快照(Threaddump),常用于定于线程长时间停顿
6. 可视化工具
6.1 JConsole
查看各种堆、方法区、线程等信息
内存标签页:相当于jstat命令,可以查看堆和方法区的情况
线程标签页:相当于jstack命令,可以查看各线程停顿情况,可以检测死锁
类标签页:查看总加载类数目以及当前加载的类的数量
VM概要标签页:各种JVM参数
6.2 VisualVM
功能最强大的运行监控和故障处理程序之一,在JConsole的基础上可以生成查看dump文件,还有更多可安装插件的功能
可以生成dump,查看实例占用空间大小
7. 排查总结
7.1 CPU过高
- jstack pid 来查看线程的详细信息
- 线程状态(关注WAITING、BLOCKED),是否大量线程等待这个资源-----停顿情况
- 死锁 (Deadlock),自动检测一下-----停顿情况
- 然后根据打印的栈信息可定位代码位置-----查看死循环问题
1、查看cpu占用高的进程pid: top -c (X 加亮排序效果 | P cpu排序 | M memory排序)
2、打印线程存储快照threaddump: jstack pid > 20231020-104300.threaddump
3、获取占用cpu高的线程tid: top -Hp pid
4、转换线程tid到十六进制: printf '%x\n' tid
4、线程快照里面查找改线程: cat xxx.dump | grep tid -C 10
7.2 频繁GC
问题一般是大量对象涌入撑满导致
- jstat -gcutile pid 查看是否频繁GC,根据次数和时间对比
- 是否堆年轻代老年代需要调优
7.3 OOM
无非就是内存泄漏,年轻代大量涌入无法清除,进入老年代也无法清除
- 生成dump快照 或 自动设置的快照-XX:-XX:+HeadDumpOnOutOfMemoryError
- 用分析工具分析:查看哪个类和实例数过大,本来就定位线程了,只需看该线程的对象信息即可
7.3 死锁
- 直接JConsole排查死锁
- jstack 查看wating on Condition、等条件状态
8. GC 配置
# 开启 GC 打印(调用 system.gc 就会打印信息)
-XX:+PrintGCDetails
# 打印日期
-XX:+PrintGCDateStamps
# 垃圾回收前程序未中断的执行时间,GC后时间置为0
-XX:+PrintGCApplicationConcurrentTime
# STW 时间
-XX:+PrintGCApplicationStoppedTime
# 打印 GC 前后堆的详细情况
-XX:+PrintHeapAtGC
# 日志配置
LOG_DIR=/logs
# GC 日志输出文件路径及命名
-Xloggc:$LOG_DIR/gc-%t.log
# 开启日志文件分割
-XX:+UseGCLogFileRotation
# 最多分割几个文件,超过之后从头文件开始写
-XX:NumberOfGCLogFiles=10
# 每个文件上限大小,超过就触发分割
-XX:GCLogFileSize=10M