1 jstack(Stack Trace forJava)作用
查看或导出 Java 应用程序中线程堆栈信息 。
线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、长时间等待外部资源等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息.
2 命令格式
jstack [ options ]
参数解释:
第一个参数:options
- -F : 当线程挂起时,使用jstack -l pid 请求不被响应时,强制输出线程堆栈
- -l : 除堆栈外,显示关于锁的附加信息,例如 ownable synchronizers
- -m : 可以同时输出java以及C/C++的堆栈信息
3 命令演示
3.1 基本命令演示
package com.example.demo;import java.io.IOException;
/*jstack:-F : 当线程挂起时,使用jstack pid 请求不被响应时,强制输出线程堆栈-l : 除堆栈外,显示关于锁的附加信息,例如 ownable synchronizers-m : 可以同时输出java以及C/C++的堆栈信息*/public class jsctackdemo {public static void main(String[] args) throws IOException {System.out.println("jstack");System.in.read();}
}
jstack -m 5756
jstack -l 5756
jstack -F 5756
4 案例演示
4.1 CPU占用过高
(1)演示代码
package com.example.demo;import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class jsctackdemo {public static void main(String[] args) throws IOException {System.out.println("start");test1();System.out.println("end");}//死循环public static void test1() {while (true) {}}}
(2)使用Process Explorer工具,找到CPU占用率高的进程的id;
(3)右击该进程,查看属性,在thread选项卡中,找到cpu占用率高的线程id
(4)把线程id转换成16进制
其中3df0就是你的线程号
(5)使用jstack -l 查看进程的线程快照
(6)在线程快照中找到指定的线程,并分析代码
上面显示了导致CPU飚高的文件位置。
4.2 等待控制台输出
(1)代码如下
package com.example.demo;import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class jsctackdemo {public static void main(String[] args) throws IOException {System.out.println("start");test2();System.out.println("end");}//等待控制台输入public static void test2() {try {System.in.read();} catch (IOException e) {e.printStackTrace();}}}
(2)使用Process Explorer工具,查看等待进程;
(3)使用jstack -l 查看进程的线程快照
4.3 死锁
(1)代码如下
package com.example.demo;import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class jsctackdemo {public static void main(String[] args) throws IOException {System.out.println("start");test3();System.out.println("end");}//死锁public static void test3() {Lock lock1 = new ReentrantLock();Lock lock2 = new ReentrantLock();new Thread(() -> {try {lock1.lock();Thread.sleep(100);lock2.lock();//需要加锁,加锁失败,锁被另外一个线程mythread2持有了} catch (InterruptedException e) {e.printStackTrace();}}, "myThread1").start();new Thread(() -> {try {lock2.lock();Thread.sleep(100);lock1.lock();//通过lock1加锁,失败,等待锁被释放} catch (InterruptedException e) {e.printStackTrace();}}, "myThread2").start();}
}
(2)使用jstack -l 查看进程的线程快照
发现两个锁在相互等待