基础故障处理工具
- Jps:显示指定系统内所有虚拟机进程
- jstat:用于收集虚拟机各方面的运行数据
- jinfo:显示虚拟机配置信息
- jmap:生成虚拟机内存转储存快照
- jhat:用于分析heapdump文件
- jstack:显示虚拟机的线程快照
1.JPS(运行的进程状态信息)
类似 ps 命令;列出正在运行的虚拟机进程,并显示虚拟机的执行主类(main函数)的名称以及这些进程的本地虚拟机唯一ID(就是PID)
这个命令非常常用,其他 JDK 工具需要依赖 jps 命令来确定要监控的虚拟机进程的 PID
#jps 命令:主要用来输出JVM中运行的进程状态信息
jps [options]
#参数:(常用 -m -l)
-q 只输出pid
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数
#例子,第一个数字是当前进程的pid (常用就是 -m -l)
jps -mvl
15329 sun.tools.jps.Jps -mvl -Dapplication.home=/usr/local/jdk1.8 -Xms8m
1466 eureka-dh-1.0.jar -Xms64m -Xmx64m
1612 demo-1.jar -Xms64m -Xmx64m
2.jstat(运行状态信息)
可以显示虚拟机进程中的 类装载、内存、垃圾回收和编译 等数据
jstat [参数] PID [可选参数]
# 可选参数包含: interval[s|ms] count
# 即查询间隔时间和次数; 如果省略可选参数,那么说明只查询一次
#示例:
jstat -gc 1314 250 20
#命令描述:每250毫秒查询一次进程1314的垃圾收集情况, 一共查询20次
jstat参数
选项 | 作用 |
---|---|
-class | 监视类装载、卸载数量、总空间以及类装载所耗费的时间 |
-gc | 监视堆状况:包含容量,已用空间和GC时间等信息 |
-gcnew | 监视新生代的GC状况 |
-gcold | 监视老年代的GC状况 |
-gcmetacapacity | 监视元空间的GC状况 |
-gcutil | 监视内容和-gc基本相同,但输出主要关注已使用空间占总空间的百分比 |
-compiler | 输出编译器编译过的方法、耗时等信息 |
-gcnewcapacity | 输出和-gcnew基本相同,主要关注使用到的最大、最小空间 |
-gcoldcapacity | 输出和-gcold基本相同,主要关注使用到的最大、最小空间 |
可选参数
可选参数包括: interval[s|ms] count
即:即 查询间隔时间 和 查询次数 ;如果省略可选参数,那么说明只查询一次
例如:
jstat -gc 1314 250 20
命令描述:每 250 毫秒查询一次进程PID为 1314 的垃圾收集情况, 一共查询20次
1.-class类加载统计
- **Loaded:**加载class的数量
- **Bytes:**所占用空间大小
- **Unloaded:**未加载数量
- **Bytes:**未加载占用空间
- **Time:**时间
[root@daiadrian ~]# jstat -class 29359
Loaded Bytes Unloaded Bytes Time
556 1087.5 0 0.0 0.12
2.-gc堆内存统计
- **S0C:**第一个 Survivor 区的大小
- **S1C:**第二个 Survivor 区的大小
- **S0U:**第一个 Survivor 区的使用大小
- **S1U:**第二个 Survivor 区的使用大小
- **EC:**Eden区的大小
- **EU:**Eden区的使用大小
- **OC:**老年代大小
- **OU:**老年代使用大小
- **MC:**方法区大小
- **MU:**方法区使用大小
- **CCSC:**压缩类空间大小
- **CCSU:**压缩类空间使用大小
- **YGC:**年轻代垃圾回收次数
- **YGCT:**年轻代垃圾回收消耗时间
- **FGC:**老年代垃圾回收次数
- **FGCT:**老年代垃圾回收消耗时间
- **GCT:**垃圾回收消耗总时间
[root@daiadrian ~]# jstat -gc 29359
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
1024.0 1024.0 0.0 0.0 8192.0 2474.5 20480.0 0.0 4480.0 774.4 384.0 75.9 0 0.000 0 0.000 0.000
3.-gcnew新生代垃圾回收统计
- **S0C:**第一个 Survivor 区大小
- **S1C:**第二个 Survivor 区的大小
- **S0U:**第一个 Survivor 区的使用大小
- **S1U:**第二个 Survivor 区的使用大小
- **TT:**对象在新生代存活的次数
- **MTT:**对象在新生代存活的最大次数
- **DSS:**期望的幸存区大小
- **EC:**Eden区的大小
- **EU:**Eden区的使用大小
- **YGC:**年轻代垃圾回收次数
- **YGCT:**年轻代垃圾回收消耗时间
[root@daiadrian ~]# jstat -gcnew 29359
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
1024.0 1024.0 0.0 0.0 15 15 0.0 8192.0 2474.5 0 0.000
4.-gcold老年代垃圾回收统计
- **MC:**方法区大小
- **MU:**方法区使用大小
- **CCSC:**压缩类空间大小
- **CCSU:**压缩类空间使用大小
- **OC:**老年代大小
- **OU:**老年代使用大小
- **YGC:**年轻代垃圾回收次数
- **FGC:**老年代垃圾回收次数
- **FGCT:**老年代垃圾回收消耗时间
- **GCT:**垃圾回收消耗总时间
[root@daiadrian ~]# jstat -gcold 29359
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
4480.0 774.4 384.0 75.9 20480.0 0.0 0 0 0.000 0.000
5.-gcmetacapacity元数据空间统计
- MCMN: 最小元数据容量
- **MCMX:**最大元数据容量
- **MC:**当前元数据空间大小
- **CCSMN:**最小压缩类空间大小
- **CCSMX:**最大压缩类空间大小
- **CCSC:**当前压缩类空间大小
- **YGC:**年轻代垃圾回收次数
- **FGC:**老年代垃圾回收次数
- **FGCT:**老年代垃圾回收消耗时间
- **GCT:**垃圾回收消耗总时间
[root@daiadrian ~]# jstat -gcmetacapacity 29359
MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT 0.0 1056768.0 4480.0 0.0 1048576.0 384.0 0 0 0.000 0.000
6.-gcutil
- **S0:**Survivor 1区当前使用比例
- **S1:**Survivor 2区当前使用比例
- **E:**Eden区使用比例
- **O:**老年代使用比例
- **M:**元数据区使用比例
- **CCS:**压缩使用比例
- **YGC:**年轻代垃圾回收次数
- **FGC:**老年代垃圾回收次数
- **FGCT:**老年代垃圾回收消耗时间
- **GCT:**垃圾回收消耗总时间
[root@daiadrian ~]# jstat -gcutil 1466
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.05 65.76 64.32 95.33 93.82 394 1.614 3 0.227 1.840
3.jmap(生成堆转存储快照)
jmap 命令用于生成堆转存储快照(一般为 dump 或 heapdump 文件),生成的快照是**当前时刻** 的内存快照
除了 jmap 命令,也可以启动 java 程序时指定参数 -XX:+HeapDumpOutOfMemoryError
,可以让虚拟机在发生OOM异常的时候自动生成 dump 文件
jmap 命令除了能够获取到堆转存储快照,还能查询 finalize 执行队列,Java 堆和永久代的详细信息(如空间使用率,当前使用哪种垃圾收集器等)
jmap [option] vmid
# option是参数
# vmid是通过 jps 命令查询出来的 ID
jmap参数
选项 | 作用 |
---|---|
-dump | 生成堆转存储快照 |
-heap | 显示堆的详细信息,如使用哪种垃圾收集器、参数配置、分代状况 |
-histo | 显示堆中对象统计信息,包括类、实例数量、合计容量 |
-permstat | 以ClassLoader为统计口径显示永久代内存状态 |
-F | 当虚拟机对-dump选项不起作用时,可以使用该命令强制生成dump文件 |
-finalizerinfo | 显示在F-Queue中等待Finalizer线程执行finalize方法的对象 |
4.jstack(堆栈跟踪工具)重要
jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照(一般称为 threaddump 或者 javacore 文件)
线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的目的通常是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等,都是导致线程长时间停顿的常见原因
线程出现停顿时通过 jstack 命令来查看各个线程的调用堆栈,就可以获知没有响应的线程到底在后台做些什么事情,或者等待着什么资源
#命令格式, vmid 即 jps 命令查出来的pid
jstack [option] vmid
参数选项:
选项 | 详解 |
---|---|
-F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
-l | 除堆栈外,显示关于锁的附加信息(发生死锁时,可以观察锁持有情况) |
-m | 如果调用到本地方法(Native方法)的话,可以显示C/C++的堆栈 |
线程死锁示例
public class Test {
public static void main(String[] args) {
String A = new String("A");
String B = new String("B");
new Thread(() -> {
synchronized (A) {
System.out.println("AAA::");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("A---B");
}
}
}).start();
new Thread(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("BBB::");
synchronized (A) {
System.out.println("B---A");
}
}
}).start();
}
}
[root@daiadrian ~]# jps -mvl
15601 sun.tools.jps.Jps -mvl -Dapplication.home=/usr/local/jdk1.8 -Xms8m
15570 Test
[root@daiadrian ~]# jstack -l 15570
输出内容:
2020-08-16 11:34:12
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.121-b13 mixed mode):
"Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007f1f68001000 nid=0x3d05 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007f1f90008800 nid=0x3cd3 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Thread-1" #9 prio=5 os_prio=0 tid=0x00007f1f900f4800 nid=0x3cdd waiting for monitor entry [0x00007f1f80ffe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at Test.lambda$main$1(Test.java:30)
- waiting to lock <0x00000000e345bed0> (a java.lang.String)
- locked <0x00000000e345bf18> (a java.lang.String)
at Test$$Lambda$2/471910020.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
"Thread-0" #8 prio=5 os_prio=0 tid=0x00007f1f900f2800 nid=0x3cdc waiting for monitor entry [0x00007f1f941b8000]
java.lang.Thread.State: BLOCKED (on object monitor)
at Test.lambda$main$0(Test.java:16)
- waiting to lock <0x00000000e345bf18> (a java.lang.String)
- locked <0x00000000e345bed0> (a java.lang.String)
at Test$$Lambda$1/791452441.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007f1f900b3000 nid=0x3cda runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f1f900b0000 nid=0x3cd9 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f1f900ad800 nid=0x3cd8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f1f900ac000 nid=0x3cd7 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f1f90079000 nid=0x3cd6 in Object.wait() [0x00007f1f947be000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e3408ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000000e3408ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
Locked ownable synchronizers:
- None
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f1f90074800 nid=0x3cd5 in Object.wait() [0x00007f1f948bf000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e3406b68> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000e3406b68> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
Locked ownable synchronizers:
- None
"VM Thread" os_prio=0 tid=0x00007f1f9006d000 nid=0x3cd4 runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f1f900b6000 nid=0x3cdb waiting on condition
JNI global references: 308
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f1f74004e28 (object 0x00000000e345bed0, a java.lang.String),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f1f740062c8 (object 0x00000000e345bf18, a java.lang.String),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at Test.lambda$main$1(Test.java:30)
- waiting to lock <0x00000000e345bed0> (a java.lang.String)
- locked <0x00000000e345bf18> (a java.lang.String)
at Test$$Lambda$2/471910020.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at Test.lambda$main$0(Test.java:16)
- waiting to lock <0x00000000e345bf18> (a java.lang.String)
- locked <0x00000000e345bed0> (a java.lang.String)
at Test$$Lambda$1/791452441.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock. # 表示当前有一个死锁的情况
jstack内容解析
"Thread-1" #9 prio=5 os_prio=0 tid=0x00007f1f900f4800 nid=0x3cdd waiting for monitor entry [0x00007f1f80ffe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at Test.lambda$main$1(Test.java:30)
- waiting to lock <0x00000000e345bed0> (a java.lang.String)
- locked <0x00000000e345bf18> (a java.lang.String)
at Test$$Lambda$2/471910020.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
"Thread-0" #8 prio=5 os_prio=0 tid=0x00007f1f900f2800 nid=0x3cdc waiting for monitor entry [0x00007f1f941b8000]
java.lang.Thread.State: BLOCKED (on object monitor)
at Test.lambda$main$0(Test.java:16)
- waiting to lock <0x00000000e345bf18> (a java.lang.String)
- locked <0x00000000e345bed0> (a java.lang.String)
at Test$$Lambda$1/791452441.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
- 这里的这两个线程内容就是代码中的两个线程信息
waiting for monitor entry
表示当前线程正在等待某个 monitor 释放nid=0x3cdd
表示当前的线程 nid 是多少,这个 nid 线程 PID 的十六进制的值(0x
表示十六进制)- 其中
java.lang.Thread.State
表示当前线程的状态,BLOCKED 表示当前线程是阻塞状态 at Test.lambda$main$1(Test.java:30)
指出了阻塞住的代码是在哪个类的哪一行- waiting to lock <0x00000000e345bed0> (a java.lang.String)
表示当前的线程在等待哪一个锁对象,这个对象是一个 String 字符串类型- locked <0x00000000e345bf18> (a java.lang.String)
表示当前线程锁定的对象,是一个字符串类型的对象
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f1f74004e28 (object 0x00000000e345bed0, a java.lang.String),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f1f740062c8 (object 0x00000000e345bf18, a java.lang.String),
which is held by "Thread-1"
Found one Java-level deadlock
表示发现了当前有一个死锁waiting to lock monitor 0x00007f1f74004e28 (object 0x00000000e345bed0, a java.lang.String)
表示当前这个线程正在等待一个 monitorwhich is held by "Thread-0"
表示等待的 monitor 被哪个线程持有(held
是hold
的过去式)
Java stack information for the threads listed above:
===================================================
"Thread-1":
at Test.lambda$main$1(Test.java:30)
- waiting to lock <0x00000000e345bed0> (a java.lang.String)
- locked <0x00000000e345bf18> (a java.lang.String)
at Test$$Lambda$2/471910020.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at Test.lambda$main$0(Test.java:16)
- waiting to lock <0x00000000e345bf18> (a java.lang.String)
- locked <0x00000000e345bed0> (a java.lang.String)
at Test$$Lambda$1/791452441.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
这里列出了死锁的详细信息,具体的解析内容查看上面解析
查看应用某个线程的堆栈信息
找出某个 Java 进程中最耗费CPU的 Java 线程并定位堆栈信息
这里的 获取线程的十六进制的值 就是上面示例的 nid 的值
#第一步:先找出Java进程ID
ps -ef | grep Test | grep -v grep
## 输出:root 11415 27727 0 14:53 pts/0 00:00:04 java Test
#第二步找出该进程内最耗费CPU的线程(此处pid是上一步输出的pid)
top -Hp pid
#从第二步找到耗费CPU最多的线程pid
##获取到十六进制值,此处的pid是top中耗费CPU最高的线程pid
printf "%x\n" pid
###假设这里得到 3df0 , 这个就是 nid
#输出进程的堆栈信息,然后根据线程ID的十六进制值grep
jstack pid | grep 3df0 -A 10
##这里结果能得到哪个类中的哪一行代码耗费CPU较高
相关内容查看上面解析