一、命令介绍

jstack是jdk自带的jvm分析工具,用于打印指定 java进程,core文件 或者远程 调试服务 的java线程栈信息,从而分析java程序性能不佳或者崩溃的问题。另外该命令是实验性的,不被支持。

jstack命令非常简单,使用自描述的帮助文档,可以快速掌握其使用方法:

jstack -help

Usage:

jstack [-l]

(to connect to running process)

jstack -F [-m] [-l]

(to connect to a hung process)

jstack [-m] [-l]

(to connect to a core file)

jstack [-m] [-l] [server_id@]

(to connect to a remote debug server)

Options:

-F to force a thread dump. Use when jstack does not respond (process is hung)

-m to print both java and native frames (mixed mode)

-l long listing. Prints additional information about locks

-h or -help to print this help message

如上,jstack命令作用的对象有三种,不同对象命令格式如下:

进程: 使用jstack [-l] 来连接到正在运行的进程,使用 jstack -F [-m] [-l] 来连接到挂起的进程。

core/executable: core文件是Linux下程序不正常退出产生的文件;executable文件是可产生core dump文件的java可执行程序;使用jstack [-m] [-l] 命令。

远程服务: jstack [-m] [-l] [server_id@]连接远程服务。

snapshoot 快照

一个活跃的java程序,其虚拟机内的线程也是活跃的,不断新建销毁,在各个线程状态之间转移。jstack命令,其实是对命令执行时刻的虚拟机线程集合做一个快照,包含了当前时刻虚拟机内所有线程方法栈。便于通过线程方法栈的行为,定位程序性能不佳或者崩溃问题。这些行为主要是线程之间的同步,如死锁,死循环,等待外部资源竞争锁的行为。通过分析离线文件或者附着到正在运行的java进程,定位问题。

一窥方法栈

一段线程方法栈信息如下:

"localhost-startStop-1-SendThread(10.0.24.14:2181)" daemon prio=10 tid=0x00002b0ee8b4e000 nid=0x4b37 waiting for monitor entry [0x00002b0ed5162000]

java.lang.Thread.State: BLOCKED (on object monitor)

at org.apache.log4j.Category.callAppenders(Category.java:204)

- waiting to lock <0x00000000db301138> (a org.apache.log4j.spi.RootLogger)

at org.apache.log4j.Category.forcedLog(Category.java:391)

at org.apache.log4j.Category.log(Category.java:856)

at org.slf4j.impl.Log4jLoggerAdapter.info(Log4jLoggerAdapter.java:305)

at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1156)

跟普通的java方法调用栈很相似,但是又多了些额外的信息,下面重点介绍这些额外的信息。

二、线程状态

jstack打印出的线程状态与java线程状态很相似,但并不是严格意义上相同。

java线程的状态图大概如下:

java线程状态

NEW: 实例化Thread类后的线程对象,不可执行。

RUNNABLE: 调用线程的start()方法后;等待CPU时间片。

RUNNING: 获得CPU时间片,正在执行。

WAITING: 线程等待其他线程执行特定操作,等待时间不确定。

TIMED_WAITING: 线程等待其他线程执行特定操作,等待时间确定。

BLOCKED: 进入同步方法或者同步代码块,没有获取到锁,进入该状态。

TERMINATED: 线程执行完毕,或者抛出未处理的异常,线程结束。

实际上,Thread的内部枚举类 java.lang.Thread.State 也对java线程状态做了介绍,其中没有 RUNNING 状态,以上是考虑CPU调度引入的这个状态。

thread states

jstack导出的线程方法栈状态也是以java线程状态为准,不过,jvm中的实际线程状态不包括NEW以及TERMINATED,只包括:

RUNNABLE

WAITING

TIMED_WAITING

BLOCKED

Monitor机制

操作系统为支持进程/线程间的同步,提供了一些基本的同步原语,其中semaphore信号量 和 mutex互斥量是其中最重要的同步原语。但是使用基础同步原语控制并发时,程序员必须维护繁琐的细节,何时应该加锁,何时应该唤醒进程/线程;为便于开发并发程序,一些高级语言支持了Monitor机制,类似语法糖,操作系统本身不支持Monitor,其实现依赖基础同步原语。

具体到高级语言Java,synchronized ,Object(wait, notify/notifyAll)等元素提供了对Monitor机制的支持。直观的代码体现是 同步方法 或者 同步代码块(虽然java的锁机制也提供线程同步,但锁机制与Monitor机制是不同的)。

下图描述了线程状态转移与Monitor的关系:

thread&monitor

整个monitor分为三个区域,处于不同区域的线程有不同的状态:

Entry Set(进入区):线程欲获取锁,获取锁成功,进入拥有者区域,否者在进入区等待锁;锁释放后,重新参与竞争锁。

The Owner(拥有者):线程成功获得锁。

Wait Set(等待区):由于必要条件不满足,线程通过调用对象的wait方法,释放锁,并进入等待区等待被notify/notifyAll唤醒。

Monitor机制中,有且仅有一个线程可以成为Monitor的拥有者,这是这个线程是 An Active Thread;处于 Entry Set以及Wait Set的线程都是 Waiting Thread。

三、线程转储堆栈分析

一条典型的jstack线程栈格式如下:

"线程名" [daemon] prio= os_prio= tid= nid= 线程动作 [线程栈的起始地址]

java.lang.Thread.State:线程状态 [(进入该状态的原因)]

方法调用栈

[-调用修饰]

Locked ownable synchronizers:

- (可持有同步器对象)

第一行说明线程相关信息,包括:

线程名。

是否守护线程,daemon标识,非守护线程没有。

线程优先级。

线程操作系统优先级。

线程id。

操作系统映射的线程id,十六进制字符串,使用这个id与实际操作系统线程id关联。

线程动作。

线程栈的起始地址。

线程动作

需要特别说明的是,线程动作,它提供的额外信息利于定位问题;线程动作包括:

runnable: 线程可执行,对应的线程状态一般为RUNNABLE, 也有例外对应TIMED_WAITING。

waiting on condition: 调用park阻塞、等待区等待。

waiting for monitor entry: 处于Monitor Entry Set,对应的线程状态一般是BLOCKED。

in Object.wait(): 处于Monitor Wait Set,状态为WAITING或TIMED_WAITING。

sleeping: 调用了sleep,休眠。

第二行是线程的状态,是java.lang.Thread.State中的一种;后面的括号里是进入该状态的原因(可选)。

方法调用栈紧随其后,重要的同步信息也会被输出。

调用修饰

调用修饰是线程方法调用过程中,重要的同步信息;调用修饰包括:

locked (目标): 使用synchronized成功获得对象锁,即Monitor的拥有者。

waiting to lock (目标): 使用synchronized获取对象锁失败,进入Entry Set等待。

waiting on (目标): 使用synchronized获取对象锁成功后,必要条件不满足,调用object.wait()进入Wait Set等待。

parking to wait for (目标): 调用park。

同步原语park比较特殊,不属于Monitor机制,他是锁机制的基础支持。由Unsafe类的native方法park实现。

最后一行是指定了 -l 选项才会输出的,额外的锁信息。表示当前线程获得的可持有同步器。由此可见Monitor机制(synchronized系列)的得天独厚,线程方法栈对Monitor机制的同步信息进行了详尽的说明。Monitor同步机制下,Locked ownable synchronizers为None。由于锁机制的Lock只是普通的java类,jvm无从得知其详尽的线程同步情况,因此使用锁机制实现的线程同步,出现问题时,不如monitor机制的同步实现,不利于辨识。

三、线程动作&调用修饰实践

分析jstack的线程方法栈,主要就是分析线程之间的同步信息,以及其方法调用栈。以上详细介绍了分析jstack的理论知识,下面从实际代码角度,分析jstack线程方法栈。

首先准备好几个同步方法,用于多线程环境下调用:

public class JStack {

// 锁机制

static Lock lock = new ReentrantLock();

// 锁条件对象

static Condition condition = lock.newCondition();

static boolean first = true;

/**

* Monitor机制下的同步

*/

static void monitorSync() {

synchronized (JStack.class) {

while (true) ;

}

}

/**

* Monitor机制下 条件等待

*

* @throws InterruptedException

*/

static void monitorSyncWait() throws InterruptedException {

synchronized (JStack.class) {

if (first) {

first = false;

while (!first) {

JStack.class.wait();

}

}

while (true) ;

}

}

/**

* lock机制下的同步

*/

static void lockSync() {

lock.lock();

try {

while (true) ;

} finally {

lock.unlock();

}

}

/**

* lock机制下条件等待

*

* @throws InterruptedException

*/

static void lockSyncAwait() throws InterruptedException {

lock.lock();

try {

if (first) {

first = false;

while (!first) {

condition.await();

}

}

while (true) ;

} finally {

lock.unlock();

}

}

}

1) Monitor机制,synchronized竞争锁

多线程synchronized竞争锁:

public static void main(String[] args) {

// Monitor机制,synchronized竞争锁

Thread monitorSync1 = new Thread(JStack::monitorSync);

monitorSync1.setName("SYNC monitor#1");

Thread monitorSync2 = new Thread(JStack::monitorSync);

monitorSync2.setName("SYNC monitor#2");

monitorSync1.start();

monitorSync2.start();

}

"SYNC monitor#2" #12 prio=5 os_prio=0 tid=0x000000001f299800 nid=0x3630 waiting for monitor entry [0x000000001fc7f000]

java.lang.Thread.State: BLOCKED (on object monitor)

at jstack.JStack.monitorSync(JStack.java:27)

- waiting to lock <0x000000076b87f128> (a java.lang.Class for jstack.JStack)

at jstack.JStack$$Lambda$2/1989780873.run(Unknown Source)

at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:

- None

"SYNC monitor#1" #11 prio=5 os_prio=0 tid=0x000000001f299000 nid=0x1854 runnable [0x000000001fb7f000]

java.lang.Thread.State: RUNNABLE

at jstack.JStack.monitorSync(JStack.java:27)

- locked <0x000000076b87f128> (a java.lang.Class for jstack.JStack)

at jstack.JStack$$Lambda$1/2093631819.run(Unknown Source)

at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:

- None

可见,Monitor机制下,使用synchronized竞争锁,

Monitor拥有者SYNC monitor#1的调用修饰是 lock,线程动作是runnable,状态是RUNNABLE

竞争锁失败的线程SYNC monitor#2的调用修饰是 waiting to lock,地址和目标与拥有者线程相同,线程动作是waiting for monitor entry(在Entry Set等待),状态是BLOCKED(在对象监视器上阻塞)。

2) 锁机制,竞争锁

多线程竞争lock锁:

public static void main(String[] args) {

// 锁机制,竞争锁

Thread lockSync1 = new Thread(JStack::lockSync);

lockSync1.setName("SYNC lock#1");

Thread lockSync2 = new Thread(JStack::lockSync);

lockSync2.setName("SYNC lock#2");

lockSync1.start();

lockSync2.start();

}

"SYNC lock#2" #12 prio=5 os_prio=0 tid=0x000000001f1e8800 nid=0x4d8 waiting on condition [0x000000001fbde000]

java.lang.Thread.State: WAITING (parking)

at sun.misc.Unsafe.park(Native Method)

- parking to wait for <0x000000076b8876d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)

at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)

at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)

at jstack.JStack.lockSync(JStack.java:52)

at jstack.JStack$$Lambda$2/1989780873.run(Unknown Source)

at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:

- None

"SYNC lock#1" #11 prio=5 os_prio=0 tid=0x000000001f1e8000 nid=0x172c runnable [0x000000001fadf000]

java.lang.Thread.State: RUNNABLE

at jstack.JStack.lockSync(JStack.java:54)

at jstack.JStack$$Lambda$1/2093631819.run(Unknown Source)

at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:

- <0x000000076b8876d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

可见,锁机制下,锁竞争同步原语都是park:

锁拥有锁线程 SYNC lock#1获取锁并没有调用修饰,而在Locked ownable synchronizers 指明其拥有的可持有同步器(锁),线程动作是runnable,状态时RUNNABLE。

竞争锁失败线程SYNC lock#2的调用修饰是parking to wait for,地址和对象正是拥有者线程持有的锁,线程动作是waiting on condition,状态是WAITING(parking),不同于Monitor未获得锁处于BLOCKED状态。

3) Monitor机制,条件对象上等待

多线程环境下,在monitor机制的条件对象上等待:

public static void main(String[] args) {

// Monitor机制,条件对象上等待

Thread monitorSyncCon1 = new Thread(() -> {

try {

monitorSyncWait();

} catch (InterruptedException e) {

// suppressed

}

});

monitorSyncCon1.setName("SYNC monitor condition#1");

Thread monitorSyncCon2 = new Thread(() -> {

try {

monitorSyncWait();

} catch (InterruptedException e) {

// suppressed

}

});

monitorSyncCon2.setName("SYNC monitor condition#2");

monitorSyncCon1.start();

monitorSyncCon2.start();

}

"SYNC monitor condition#2" #12 prio=5 os_prio=0 tid=0x000000001ed0b000 nid=0x31f8 runnable [0x000000001f6ff000]

java.lang.Thread.State: RUNNABLE

at jstack.JStack.monitorSyncWait(JStack.java:44)

- locked <0x000000076b87f348> (a java.lang.Class for jstack.JStack)

at jstack.JStack.lambda$main$1(JStack.java:110)

at jstack.JStack$$Lambda$2/1989780873.run(Unknown Source)

at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:

- None

"SYNC monitor condition#1" #11 prio=5 os_prio=0 tid=0x000000001ed0a000 nid=0x30d0 in Object.wait() [0x000000001f5fe000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x000000076b87f348> (a java.lang.Class for jstack.JStack)

at java.lang.Object.wait(Object.java:502)

at jstack.JStack.monitorSyncWait(JStack.java:41)

- locked <0x000000076b87f348> (a java.lang.Class for jstack.JStack)

at jstack.JStack.lambda$main$0(JStack.java:102)

at jstack.JStack$$Lambda$1/2093631819.run(Unknown Source)

at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:

- None

可见,Monitor机制下,在条件对象上等待:

在条件对象上等待的线程SYNC monitor condition#1, 首先 locked <0x000000076b87f348>获得锁成为拥有者,接着主动调用wait方法,waiting on <0x000000076b87f348>后释放锁,在条件对象上等待,进入Wait Set,线程动作是in Object.wait(),状态是WAITING(在对象监视器上等待)。

此时成为拥有者的线程SYNC monitor condition#2,在线程SYNC monitor condition#1主动放弃锁后 locked <0x000000076b87f348>获得同一个锁,线程动作是runnable,线程状态是RUNNABLE。

4) Lock机制,条件对象上等待

多线程环境下,竞争lock对象的条件对象:

public static void main(String[] args) {

// Lock机制,条件对象上等待

Thread lockSyncCon1 = new Thread(() -> {

try {

lockSyncAwait();

} catch (InterruptedException e) {

// suppressed

}

});

lockSyncCon1.setName("SYNC lock condition#1");

Thread lockSyncCon2 = new Thread(() -> {

try {

lockSyncAwait();

} catch (InterruptedException e) {

// suppressed

}

});

lockSyncCon2.setName("SYNC monitor condition#2");

lockSyncCon1.start();

lockSyncCon2.start();

}

"SYNC monitor condition#2" #12 prio=5 os_prio=0 tid=0x000000001f1aa800 nid=0x135c waiting on condition [0x000000001fb9e000]

java.lang.Thread.State: WAITING (parking)

at sun.misc.Unsafe.park(Native Method)

- parking to wait for <0x000000076b8894f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)

at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)

at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)

at jstack.JStack.lockSyncAwait(JStack.java:71)

at jstack.JStack.lambda$main$1(JStack.java:131)

at jstack.JStack$$Lambda$2/1989780873.run(Unknown Source)

at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:

- None

"SYNC lock condition#1" #11 prio=5 os_prio=0 tid=0x000000001f1aa000 nid=0x330c runnable [0x000000001fa9f000]

java.lang.Thread.State: RUNNABLE

at jstack.JStack.lockSyncAwait(JStack.java:74)

at jstack.JStack.lambda$main$0(JStack.java:123)

at jstack.JStack$$Lambda$1/2093631819.run(Unknown Source)

at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:

- <0x000000076b887af0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

可见,在lock条件对象上等待:

一开始获得锁的线程SYNC monitor condition#2,后来放弃锁在锁的条件对象上等待,方法调用栈中并没有提现这一个过程,只是在最后说明,该线程parking to wait for <0x000000076b8894f8>,线程动作是 waiting on condition,状态是WAITING (parking)

由于线程SYNC monitor condition#2主动释放锁而获得锁的线程SYNC lock condition#1,Locked ownable synchronizers指示它获得一个锁,处于RUNNABLE状态。

以上,wait或者await方法加上一个超时时间,WAITING状态变为TIMED_WAITING状态

4) sleep

try {

Thread.sleep(50_000);

} catch (InterruptedException e) {

// suppressed

}

"main" #1 prio=5 os_prio=0 tid=0x00000000036e2800 nid=0x2aa8 waiting on condition [0x00000000035df000]

java.lang.Thread.State: TIMED_WAITING (sleeping)

at java.lang.Thread.sleep(Native Method)

at jstack.JStack.main(JStack.java:141)

Locked ownable synchronizers:

- None

REFER TO

linux分析jstack,jstack命令以及线程转储堆栈分析相关推荐

  1. java jstack 分析工具_JVM分析工具——jstack实践

    JVM分析工具--jstack实践 一.不是万能的jstack 企业级的Java程序中,java程序出现性能低下甚至直接崩溃时,就不能像解决业务bug那样,从行为表现上来修复问题了.性能问题一般是jv ...

  2. jmc线程转储_Java线程转储– VisualVM,jstack,kill -3,jcmd

    jmc线程转储 Java Thread dump is list of all the threads active in the JVM. Java线程转储是JVM中所有活动线程的列表. Java线 ...

  3. 如何分析线程转储–线程堆栈跟踪

    本文是" 线程转储"分析系列的第5部分. 到目前为止,您已经了解了线程的基本原理以及它们与Java EE容器和JVM的交互. 您还学习了HotSpot和IBM Java VM的不同 ...

  4. java 线程转储_获取Java线程转储的常用方法(推荐)

    1. 线程转储简介 线程转储(Thread Dump)就是JVM中所有线程状态信息的一次快照. 线程转储一般使用文本格式, 可以将其保存到文本文件中, 然后人工查看和分析, 或者使用工具/API自动分 ...

  5. java查询线程状态命令_JAVA 线程死锁,以及linux 命令和jstack 命令 查看线程死锁状态信息...

    /* * Copyright (C) 2009 The doctor Authors * https://github.com/doctorwho1986 * * Licensed under the ...

  6. JVM监控工具介绍jstack, jconsole, jinfo, jmap, jdb, jsta (Linux 如何查看进程的各线程的CPU占用 )

    http://dolphin-ygj.iteye.com/blog/366216 JVM监控工具介绍jstack, jconsole, jinfo, jmap, jdb, jsta 博客分类: 调优 ...

  7. 使用jstack命令dump线程信息

    jstack是java虚拟机自带的一种堆栈跟踪工具,jstack用于打印出给定的java进程ID或远程调试服务的java堆栈信息. 线程状态 想要通过jstack命令来分析线程的情况的话,首先要知道线 ...

  8. 20210519 使用jstack命令排查线程死锁问题

    问题:  针对线上多线程死锁.阻塞,跑着跑着就卡住了:查看线上线程池的状态: jstack主要用于生成java虚拟机当前时刻的线程快照.线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合 ...

  9. jvm线程分析命令_JVM:如何分析线程转储

    jvm线程分析命令 本文将教您如何分析JVM线程转储,并查明问题的根本原因. 以我的观点,线程转储分析是掌握Java EE生产支持的任何个人最重要的技能. 您可以从线程转储快照中获取的信息量通常远远超 ...

最新文章

  1. 27.3. source code
  2. 华人小哥控诉机器学习四大 Boring!CS 博士:深有同感,正打算退学
  3. 密码错误时alert弹出_密码重置用户流程4部曲
  4. 简单点亮发光二极管实例(位操作)
  5. iOS上传头像, 相册权限,相册权限,拍照上传,相册选择图片,拍照页面语言设置,保存到相册...
  6. 【ElasticSearch】深入理解 ElasticSearch Doc Values
  7. CSS3伸缩布局的应用
  8. 第一次没有写出来的SQL
  9. Sharepoint 2013 表单验证四(添加用户到表单验证数据库)
  10. POJ2653 Pick-up sticks
  11. 红帽RHCE之查看进程
  12. Lory Carousel滑块具有CSS动画和触摸支持
  13. TortoiseGit commands(命令)(完整中文翻译)
  14. 软件本身呢对shellcode 检查 SEH Exploit学习
  15. Web前端--HTML+CSS+JS实现圣诞抓礼物小游戏
  16. VC驿站 - WwW.CcTry.CoM
  17. 小白都能懂的 玩转docker系列之 Docker网络详解(超详细)
  18. Qt快捷键之大小写切换
  19. 帕累托改进和帕累托最优、卡尔多-希克斯改进
  20. 【转载】CSS 选择器参考手册

热门文章

  1. 惠普暗夜精灵3plus配置ubuntu18.0.4、cuda9.0、cudnn7.0、anaconda(python2.7)、tensorflow-gpu1.8、keras、opencv等
  2. potplay录制摄像头视频
  3. Club Med桂林度假村拍摄金牌真人秀节目“奔跑吧”第八季
  4. java 集合,实现获取两个list交集,并集,差集
  5. 滴滴在职iOS开发者,浅谈程序员如何进行高效的技能提升?进阶架构师?
  6. 哈工大C语言程序设计精髓-拆分英文名
  7. php 多线程处理_PHP中异步处理,多线程或多任务实现
  8. ubuntu18.04 搭建LDAP服务器
  9. DevExpress Winform通用控件开发总结
  10. Total Commander中webdav挂载中文目录无法打开问题解决