文章目录
命令工具
jps检索进程
jps -l 输出主类的全名,如果进程执行的是Jar包则输出Jar路径
jps -v 输出虚拟机进程启动时JVM参数
jstat分代信息运行状态信息
监视虚拟机各种运行状态信息的命令行工具
jstat -gc pid [刷新毫秒] [打印条数]
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
jinfo实时地查看和调整虚拟机各项参数
jinfo pid
jinfo -flag MetaspaceSize <进程号>查看元空间大小
jmap生成堆转储快照
查看类的数量和所占大小,输出前30行
jmap -histo pid | head -30 或者|more分页查看
查看堆内存情况
jmap -heap pid
生成堆内存快照到指定目录
jmap -dump:format=b,file=/home/path/yyyyMMdd.hprof pid
通常在启动java程序的时候设置启动参数,发生OOM了自动dump
-XX:+HeapDumpOnOutOfMemoryError
指定输出目录
-XX:HeapDumpPath=/home/xx/dump
jhat分析jmap生成的堆转储快照(耗性能,用的少)
jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,可以在浏览器中查看
localhost:7000
jhat /xx/xx.hprof
jstack生成虚拟机当前时刻的线程快照(可检查线程死锁)
可以结合|more分页
jstack -l pid
线程死锁,可以看到具体类方法行数发生的死锁
并打印各个线程调用方法信息
日志解析
parallelScavenge+parallelOld日志解析
启动参数:
‐XX:+PrintGCDetails
日志:
[GC (Allocation Failure) [PSYoungGen: 65536K->4048K(76288K)] 65536K->4056K(251392K), 0.0234656 secs] [Times: user=0.09 sys=0.03, real=0.03 secs]
[GC (Allocation Failure) [PSYoungGen: 69584K->5248K(141824K)] 69592K->5264K(316928K), 0.0243941 secs] [Times: user=0.11 sys=0.03, real=0.03 secs]
[GC (GCLocker Initiated GC) [PSYoungGen: 41701K->5456K(141824K)] 41717K->5472K(316928K), 0.0210557 secs] [Times: user=0.11 sys=0.02, real=0.02 secs]
[GC (Metadata GC Threshold) [PSYoungGen: 14037K->5456K(272896K)] 14053K->5480K(448000K), 0.0204318 secs] [Times: user=0.13 sys=0.03, real=0.02 secs]
[Full GC (Metadata GC Threshold) [PSYoungGen: 5456K->0K(272896K)] [ParOldGen: 24K->5262K(75776K)] 5480K->5262K(348672K), [Metaspace: 20667K->20667K(1069056K)], 0.0186367 secs] [Times: user=0.08 sys=0.00, real=0.02 secs]
第一行日志:
GC (Allocation Failure)
给YoungGen内存分配失败触发GC
[PSYoungGen: 65536K->4048K(76288K)]
65536K=年轻代收集前占用的大小
4048K=收集后占用的大小
76288K=年轻代总共大小
65536K->4056K(251392K), 0.0234656 secs]
65536K=垃圾收集之前Java堆占用的大小
4056K=垃圾收集之后Java堆占用的大小
251392K=总堆大小1509376K
0.0234656 secs=垃圾收集过程所消耗的时间
[Times: user=0.09 sys=0.03, real=0.03 secs]
user=0.09=GC用户耗时(用户态)
sys=0.03=GC系统耗时(内核态)
real=0.03=GC实际耗时
第二行日志:
可以看出年轻代和,总的java堆内存总大小更大了,执行过扩容
扩容也是需要耗时间,可以在启动时指定起始大小减少频繁扩容
第三行日志:
GC (GCLocker Initiated GC)
使用本地方法JNI函数访问JVM中的字符串或数组数据(代码在临界区执行),必须保证原始数据不被修改,
防止其它线程的操作、或发生GC回收改字符串对象。利用GC_locker加锁,保证临界区代码的正确执行。
有线程进入了临界区,导致触发的YGC被丢弃。当线程执行完临界区的代码之后,执ReleaseStringCritical方法,
离开临界区,这个时候发现触发过gc,这个时候再触发gc
第四行日志:
GC (Metadata GC Threshold)
元数据(方法区)空间不足触发GC
第五行日志:
Full GC (Metadata GC Threshold)
元数据(方法区)空间不足触发的Full GC
Class.forName动态动态加载类信息,实际上是在占用元数据区的空间,并发较大加载过多动态类信息可能导致元空间outOfMemoryError MetaSpace溢出
Class.forName慎用
[ParOldGen: 24K->5262K(75776K)]
老年代收集信息
[Metaspace: 20667K->20667K(1069056K)]
方法区收集信息
其他信息都是上面解析一样的格式方式解读即可
G1日志解析
可视化工具
jconsole
jvisualvm
jvisualvm比jconsole更强大,一般生产上不直接用图像工具
专业工具
arthas
排查流程
top查看占用较大资源进程
top -Hp pid 找到占用较大的线程编号 结合 jstack pid查看对应的线程
jstack查看的可能是 gc线程 或者 业务线程
如果是gc -> 一定是频繁gc导致线程飙高 -> 读gc日志
如果是业务 -> 查看对应线程调用的方法