命令介绍:
jstack全称叫Java Stack Trace,Java的堆栈跟踪工具,用于生成java虚拟机当前时刻的线程快照。功能主要有两个,1.分析死锁;2.分析CPU过高问题。
线程快照里留意下面几种状态
- 死锁,Deadlock(重点关注)
- 等待资源,Waiting on condition(重点关注)
- 等待获取管程,Waiting on monitor entry(重点关注)
- 阻塞,Blocked(重点关注)
- 执行中,Runnable
- 暂停,Suspended
- 对象等待中,Object.wait() 或 TIMED_WAITING
- 停止,Parked
Jstack 命令格式如下
jstack [ option ] pid 查看当前时间点,指定进程的dump堆栈信息。
jstack [ option ] pid > 文件 将当前时间点的指定进程的dump堆栈信息,写入到指定文件中。# 注:若该文件不存在,则会自动生成; 若该文件存在,则会覆盖源文件。
jstack [ option ] executable core 查看当前时间点,core文件的dump堆栈信息。
jstack [ option ] [server_id@]<remote server IP or hostname> 查看当前时间点,远程机器的dump堆栈信息。
option 参数如下:
-F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
-m | 打印java和native c/c++框架的所有栈信息。可以打印JVM的堆栈,以及Native的栈帧,一般应用排查不需要使用。 |
-l | 除堆栈外,显示关于锁的附加信息,在发生死锁时可以用jstack -l pid来观察锁持有情况 |
参数解释:
- executable: 产生core dump的java可执行程序
- core: 将被打印信息的core dump文件
- remote-hostname-or-IP: 远程debug服务的主机名或ip
- server-id: 唯一id,假如一台主机上多个远程debug服务
以下用两个实战案例来分析这两种情况:
实战案例1:jstack分析死锁
案例代码
public class DeadLockTest {
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(6000);
} catch (InterruptedException e) {
}
synchronized (lock2) {
System.out.println("thread1 end");
}
}
}).start();
new Thread(() -> {
synchronized (lock2) {
try {
System.out.println("thread2 begin");
Thread.sleep(6000);
} catch (InterruptedException e) {
}
synchronized (lock1) {
System.out.println("thread2 end");
}
}
}).start();
System.out.println("main thread end");
}
}
排查命令
jstack 进程ID
或
jstack 进程ID| grep 'BLOCKED' -A 15 --color
结果分析:
通过结果可以看出Thread-3线程锁住了0x00000000f6360238,然后在等待0x00000000f6360228,而Thread-2却却相反,从下面每个线程的第一行ThreadController.java:42可以看到死锁的代码在哪个地方,进而排查出问题所在。
"Thread-3":
at com.example.threaddemo.ThreadController.lambda$getThread$1(ThreadController.java:42)
- waiting to lock <0x00000000f6360228> (a java.lang.Object)
- locked <0x00000000f6360238> (a java.lang.Object)
at com.example.threaddemo.ThreadController$$Lambda$464/550716470.run(Unknown Source)
at java.lang.Thread.run(Thread.java:750)
"Thread-2":
at com.example.threaddemo.ThreadController.lambda$getThread$0(ThreadController.java:30)
- waiting to lock <0x00000000f6360238> (a java.lang.Object)
- locked <0x00000000f6360228> (a java.lang.Object)
at com.example.threaddemo.ThreadController$$Lambda$463/1326723928.run(Unknown Source)
at java.lang.Thread.run(Thread.java:750)
但用jstack分析死锁的话,在调用链很长的情况下,也很难定位,这时可以用使用Arthas进行分析
thread -b
可以准确知道: 死锁在代码的中的第几行
实战案例2:jstack分析CPU过高问题
案例代码
public class CpuMath{
public static final int initData = 666;
public static User user = new User();
public int compute() {
int a = 1;
int b = 2;
int c = (a + b) * 100;
return c;
}
public static void main(String[] args) {
CpuMath cpuMath= new CpuMath();
while (true){
cpuMath.compute();
}
}
}
排查命令流程
1. top
获取各个进程的CPU和内存情况,并且找到CPU飙高的进程ID,比如进程ID=10843
2. top -p 进程ID
显示你的java进程的CPU和内存占用情况
3. H
按H,可以获取每个线程的CPU和内存占用情况,如果线程ID=46915
4. jstack 进程ID | grep -A 10 十六进制的线程ID
通过上面的命令,可以得到线程的堆栈信息后10行信息,从堆栈中可以发现导致CPU飙高的调用方法。从打印的堆栈信息可以看出,导致CPU飚高的代码在ThreadController类getThread方法的第21行