文章目录
- 1. 前提准备
- 2. Jmap
- 3. Jstack
- 4. Jinfo
- 5. Jstat(重点)
- 6. 案例分析
1. 前提准备
任意启动一个web项目,这里我已经启动好了
2. Jmap
map命令用于生成堆转储快照,有时候也成为heapdump或者dump文件。Jmap不仅仅可以获取dump文件,还可以查询finalize执行队列,Java堆和永久代的详细信息,如空间使用率、当时用的是那种收集器等。
使用jps查看我们的进程号为99646
jps
使用jmap查看信息
jmap -histo 99646
发现终端查看信息比较不清楚,我们可以将输出放入到一个日志文件中
jmap -histo 99646 > ./log.txt
- num:序号
- instances:实例数量
- bytes: 占用空间大小
- class name: 类名称
查看堆信息
jmap -heap 99646
导出堆的快照,并可以将快照导入到jvisualvm分析
jmap -dump:format=b,file=eureka.hprof 99646
3. Jstack
jstack命令用于打印指定Java进程、核心文件或远程调试服务器的Java线程的Java堆栈跟踪信息。jstack命令可以生成JVM当前时刻的线程快照。线程快照是当前JVM内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
下面模拟程序死锁情况,线程获得锁1然后想获得锁2,线程2获得锁2然后想获得锁1,这样会出现相互等待而产生死锁
public class jvmtestMain {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (lock1) {
try{
System.out.println("thread1 begin");
Thread.sleep(5000);
} catch (InterruptedException e) { }
synchronized (lock2) {
System.out.println("thread1 end");
}
}}).start();
new Thread(()->{
synchronized (lock2) {
try{
System.out.println("thread2 begin"); Thread.sleep(5000);
} catch (InterruptedException e) { }
synchronized (lock1) { System.out.println("thread2 end"); }
}}).start();
System.out.println("main thread end");
}
}
jstack打印线程堆栈信息
jstack 4276
4. Jinfo
jinfo是java虚拟机自带的Java配置信息工具,可以实时地查看和调整虚拟机的各项参数。
- jinfo [ option ] pid 连接到正在运行的进程
- jinfo [ option ] executable core 连接到核心文件
- jinfo [ option ] [ servier-id ] remote-hostname-or-IP 要连接到远程DEBUG服务器
jinfo 6123
5. Jstat(重点)
jstat命令可以查看堆内各个部分的使用量就,以及加载类的数量,命令的格式如下:
jstat [-命令选项] [vmid][间隔时间(毫秒)][查询次数]
- 垃圾回收统计
jstat -gc 21968
- S0C:第一个幸存区的大小,单位KB
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- OC:老年代大小
- OU:老年代使用大小
- MC:方法区大小(元空间)
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间,单位s
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间,单位s
- GCT:垃圾回收消耗总时间,单位s
#表示我们要执行10次这个命令,且每隔1s钟执行1次
jstat -gc 21968 1000 10
6. 案例分析
系统频繁Full GC导致系统卡顿是怎么回事?
- 机器内核:2核4G
- JVM内存大小:2G
- 系统运行时间:7天
- 期间发生的Full GC次数和耗时:500多次 200多秒
- 期间发生的Young GC次数和耗时:1万多次, 500多秒
大概算下来每天会发生70次Full GC,平局每小时3次,每次Full GC在400毫秒左右。每天会发生1000多次Young GC,每分钟会发生1次,每次Young GC在50毫秒左右
JVM参数配置如下:
-Xms1536M -Xmx1536M -Xmn512M -Xss256K -XX:SurvivorRatio=6
-XX:MetaspaceSize=256M
-XX:MaxMetaspaceSize=256M
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
- 每分钟发生一次Young GC说明一个问题:每分钟Eden区就会被新对象放满,而触发Young GC,即线程运行每秒会产生6MB对象
- 每个小时产生3次Full GC:20分钟一次Full GC,意味着20分钟有700多M对象挪到老年代(CMSInitiatingOccupancyFraction=75 ),这也导致full GC的原因,且full GC把这些移动到老年代的对象基本都杀死了(因为每20分钟做一次full GC),可能的原因如下:
- 大对象直接进入老年代(这得依据系统实际进行分析)
- 长期存活对象直接进入老年代
- 老年代担保机制
- 动态年龄判断机制