jstack使用

使用jstack观察虚拟机栈情况

jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照,查看Java线程的调用堆栈的,可以用来分析线程问题(如死锁)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合.

生成线程快照的目的主要是定位线程长时间出现停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的原因。线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者在等待些什么资源。

以检查线程死锁为例:

public class Main_Work_2_1 {
    public static void main(String[] args) throws InterruptedException {
        Map<String, String> resource1 = Map.of("name", "resource1");
        Map<String, String> resource2 = Map.of("name", "resource2");

        Thread thread1 = new Thread(() -> runOverride(resource1, resource2), "线程1");
        Thread thread2 = new Thread(() -> runOverride(resource2, resource1), "线程2");

        thread1.start();
        thread2.start();

    }

    /**
     * run方法运行实例,主要是对先获得哪个锁做限制
     * @param first
     * @param second
     */
    public static void runOverride(Map<String, String> first, Map<String, String> second) {
        Thread threadName = Thread.currentThread();
        synchronized (first) {
            System.out.println(threadName + " get " + first.get("name"));

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(threadName + " waiting get" + second.get("name"));

            synchronized (second) {
                System.out.println(threadName + " get " + second.get("name"));
            }
        }
    }

}

运行输出:

Thread[线程1,5,main] get resource1
Thread[线程2,5,main] get resource2
Thread[线程2,5,main] waiting getresource1
Thread[线程1,5,main] waiting getresource2

代码分析:线程1 通过 synchronized (first)获得resource1的监视器锁,然后通过Thread.sleep(1000);让线程1 休眠 1s;为的是让线程 2得到执行,然后获取到 resource2的监视器锁。线程 1 和线程 2休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。

使用jstack进行栈情况分析:

首先使用jps获取目标进程,可以看出Main_Work_2的进程pid为10566;

jps

在这里插入图片描述

然后查看对应进程栈内容:

jstack 10566

运行结果部分截图如下:

图一可以看出线程1处于BLOCKED状态,- waiting to lock <0x000000008df738d0>表示正在等待锁<0x000000008df738d0>,并且- locked <0x000000008df73878>表示正持有锁<0x000000008df73878>;线程2也处于BLOCKED状态,- waiting to lock <0x000000008df73878>表示正在等待锁<0x000000008df73878>,并且- locked <0x000000008df738d0>表示正持有锁<0x000000008df738d0>

图二可以看出找到一个死锁,并且可以很明显的知道线程1在等待一个锁,该锁已被线程2获取;线程2也在等待一个锁,该锁已被线程1获取。
在这里插入图片描述
在这里插入图片描述

如果将死锁代码修改成正常代码后,在进程正常运行时,再一次使用stack查看的话,就会发现运行结果正常了。

也可以使用一下命令将结果输出到文件当中,方便记录和传阅。

jstack pid > jstackOut.txt

jstack命令

命令格式

jstack [ option ] pid
jstack [ option ] executable core
jstack [ option ] [server-id@] remote-hostname-or-IP

常用参数说明

1)options:

executable:Java executable from which the core dump was produced.(可能是产生core dump的java可执行程序)

core:将被打印信息的core dump文件

remote-hostname-or-IP:远程debug服务的主机名或ip

server-id:唯一id,假如一台主机上多个远程debug服务

2)基本参数:

-F:当’jstack [-l] pid’没有相应的时候强制打印栈信息,如果直接jstack无响应时,用于强制jstack),一般情况不需要使用

-l:长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表,会使得JVM停顿得长久得多(可能会差很多倍,比如普通的jstack可能几毫秒和一次GC没区别,加了-l 就是近一秒的时间),-l 建议不要用。一般情况不需要使用

-m:打印java和native c/c++框架的所有栈信息.可以打印JVM的堆栈,显示上Native的栈帧,一般应用排查不需要使用

-h | -help:打印帮助信息

3)pid

pid 需要被打印配置信息的java进程id,可以用jps查询.

线程状态

想要通过jstack命令来分析线程的情况的话,首先要知道线程都有哪些状态,下面这些状态是我们使用jstack命令查看线程堆栈信息时可能会看到的线程的几种状态:

NEW:未启动的。不会出现在Dump中。

RUNNABLE:在虚拟机内执行的。运行中状态,可能里面还能看到locked字样,表明它获得了某把锁。

BLOCKED:受阻塞并等待监视器锁,被某个锁(synchronizers)给block住了。

WATING:无限期等待另一个线程执行特定操作;等待某个condition或monitor发生,一般停留在park(), wait(), sleep(),join() 等语句里。

TIMED_WATING:有时限的等待另一个线程的特定操作;和WAITING的区别是wait() 等语句加上了时间限制 wait(timeout)。

TERMINATED:已退出的。

调用修饰

表示线程在方法调用时,额外的重要的操作。线程Dump分析的重要信息,修饰上方的方法调用。

locked <地址> 目标:使用synchronized申请对象锁成功,监视器的拥有者。

waiting to lock <地址> 目标:使用synchronized申请对象锁未成功,在进入区等待。

waiting on <地址> 目标:使用synchronized申请对象锁成功后,释放后在等待区等待。

parking to wait for <地址> 目标:park是基本的线程阻塞原语,不通过监视器在对象上阻塞。随concurrent包会出现的新的机制,与synchronized体系不同。

线程动作

线程状态产生的原因

runnable: 状态一般为RUNNABLE。

in Object.wait(): 等待区等待,状态为WAITING或TIMED_WAITING。

waiting for monitor entry: 进入区等待,状态为BLOCKED。

waiting on condition: 等待区等待、被park。

sleeping: 休眠的线程,调用了Thread.sleep()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值