jstack java堆栈跟踪工具
1、jstack介绍
jstack
(stack trace for java
)是java虚拟机自带的一种堆栈跟踪工具。
jstack
主要用于生成java虚拟机当前时刻的线程快照,线程快照是当前java虚拟机内每一条线程正在执行的方法
堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源
导致的长时间等待等。
线程出现停顿的时候通过jstack
来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事
情,或者等待什么资源。
jstack
主要分为两个功能:
- 针对活着的进程做本地的或远程的线程dump
- 针对core文件做线程dump
当指定的进程在64位Java虚拟机上运行时,可能需要指定-J-d64
选项,例如:jstack -J-d64 -m pid
。
2、线程状态
线程通常有下面5中状态:
1、新建状态(New): 新创建了一个线程对象。
2、就绪状态(Runnable) :线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可
运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running): 就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就
绪状态,才有机会转到运行状态。阻塞的情况分三种:
等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池
中。
其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。
当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
关于线程状态,具体也可以查看:java.lang.Thread.State
类。
想要通过jstack
命令来分析线程的情况的话,首先要知道线程都有哪些状态,下面这些状态是我们使用jstack命
令查看线程堆栈信息时可能会看到的线程的几种状态:
- RUNNABLE,线程运行中或I/O等待
- BLOCKED,线程被阻塞,在等待monitor锁(synchronized关键字)
- TIMED_WAITING ,线程在等待唤醒,但设置了时限
- WAITING ,线程在无限等待唤醒
在线程的堆栈中,需要特别留意以下几种状态:
- Deadlock: 死锁(重点关注)
- Waiting on condition: 等待资源(重点关注)
- Waiting on monitor entry : 等待获取监视器(重点关注)
- Blocked :阻塞(重点关注)
- Runnable :执行中
- Suspended : 暂停
- Object.wait()或TIME_WAITING :对象等待中
- Parked : 停止
3、jstack命令格式
jstack [ option ] pid
jstack [ option ] executable core
jstack [ option ] [server-id@]remote-hostname-or-IP
pid
:Java进程的ID,可以通过jps
命令查询到。
executable
: 产生core dump的Java可执行程序。
core
:要打印的堆栈跟踪的核心文件。
server-id
:当多个DEBUG服务器在同一远程主机上运行时,可使用的可选唯一ID。
remote-hostname-or-IP
:远程DEBUG的服务器主机名或IP地址。
pid
: 需要被打印配置信息的java进程id,可以用jps查询。
常用参数说明:
-F
:当 jstack [-l] pid 没有响应时,强制打印一个堆栈转储。
-l
:打印关于锁的其他信息,比如拥有的java.util.concurrent ownable同步器的列表,会使得
JVM停顿得长久得多。
-m
:打印包含Java和本机C/ C++帧的混合模式堆栈跟踪。
-h
:打印帮助信息。
-help
:打印帮助信息。
3.1 jstack pid
第一行各个单词的解析,
-
daemon
:线程名称 。 -
prio
:线程优先级 -
tid
:指Java Thread id。 -
nid
:指native线程的id。。 -
[0x000000002369e000]
:线程栈起始地址。
3.2 死循环
package com.example.controller;
public class Test2 {
public static void main(String[] args) {
while (true) {
}
}
}
3.3 Object.wait()情况
package com.example.controller;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test3 {
static class TestTask implements Runnable {
@Override
public void run() {
synchronized (this) {
try {
//等待被唤醒
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ExecutorService ex = Executors.newFixedThreadPool(1);
ex.execute(new TestTask());
}
}
3.4 死锁情况
package com.example.controller;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test4 {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
ExecutorService ex = Executors.newFixedThreadPool(2);
// 起10个线程
for (int i = 0; i < 10; i++) {
int order = i % 2 == 0 ? 1 : 0;
ex.execute(new TestTask(order, obj1, obj2));
}
}
static class TestTask implements Runnable {
private Object obj1;
private Object obj2;
private int order;
public TestTask(int order, Object obj1, Object obj2) {
this.order = order;
this.obj1 = obj1;
this.obj2 = obj2;
}
public void test1() throws InterruptedException {
synchronized (obj1) {
synchronized (obj2) {
System.out.println("test。。。");
}
}
}
public void test2() throws InterruptedException {
synchronized (obj2) {
synchronized (obj1) {
System.out.println("test。。。");
}
}
}
@Override
public void run() {
while (true) {
try {
if (this.order == 1) {
this.test1();
} else {
this.test2();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
3.5 等待io
package com.example.controller;
import java.io.IOException;
import java.io.InputStream;
public class Test5 {
public static void main(String[] args) throws IOException {
InputStream is = System.in;
int i = is.read();
System.out.println("exit。");
}
}