【JVM】jstack命令

本文介绍了如何使用jstack命令来诊断Java程序的死锁和CPU过高问题。jstack是一个用于打印Java进程线程堆栈信息的工具,对于定位线程死锁和CPU资源过度消耗的原因非常有用。通过分析线程状态,如runnable、deadlock、blocked等,可以找出程序的瓶颈。实战案例展示了如何通过jstack分析死锁和CPU过高问题的具体步骤,帮助开发者有效排查和解决性能问题。

前言

如果有一天,你的Java程序长时间停顿,也许是它病了,需要用jstack拍个片子分析分析,才能诊断具体什么病症,是死锁综合征,还是死循环等其他病症,本文我们一起来学习jstack命令~

  • jstack 的功能
  • jstack用法
  • 线程状态等基础回顾
  • 实战案例1:jstack 分析死锁
  • 实战案例2:jstack 分析CPU 过高

jstack 的功能

jstack是JVM自带的Java堆栈跟踪工具,它用于打印出给定的java进程ID、core file、远程调试服务的Java堆栈信息.

jstack prints Java stack traces of Java threads for a given Java process or core file or a remote debug server.

  • jstack命令用于生成虚拟机当前时刻的线程快照。
  • 线程快照是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。
  • 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。
  • 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。
  • 另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

jstack用法

jstack 命令格式如下

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

参数解释

  • 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服务

最常用的是

jstack [option] // 打印某个进程的堆栈信息

option参数说明如下:

选项作用
-F当正常输出的请求不被响应时,强制输出线程堆栈
-m如果调用到本地方法的话,可以显示C/C++的堆栈
-l除堆栈外,显示关于锁的附加信息,在发生死锁时可以用jstack -l pid来观察锁持有情况

注意:Jstack 命令查看的pid进程,需与当前的登陆用户保持一致,否则会报找不到Pid文件的错误,如下。

root@java_test-c8db49f4f-vh4m5:/java_test# jstack 11
11: Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding

可以通过 su 用户名 命令切换登陆的用户

线程状态等基础回顾

线程状态简介
jstack用于生成线程快照的,我们分析线程的情况,需要复习一下线程状态吧,拿小凳子坐好,复习一下啦~

Java语言定义了6种线程的状态:

New:创建后尚未启动的线程处于这种状态,不会出现在Dump中。
RUNNABLE:包括Running和Ready。线程开启start()方法,会进入该状态,在虚拟机内执行的。
Waiting:无限的等待另一个线程的特定操作。
Timed Waiting:有时限的等待另一个线程的特定操作。
阻塞(Blocked):在程序等待进入同步区域的时候,线程将进入这种状态,在等待监视器锁。
结束(Terminated):已终止线程的线程状态,线程已经结束执行。

Monitor 监视锁
因为Java程序一般都是多线程运行的,Java多线程跟监视锁环环相扣,所以我们分析线程状态时,也需要回顾一下Monitor监视锁知识。
有关于线程同步关键字Synchronized与监视锁的爱恨情仇,有兴趣的伙伴可以看一下我这篇文章
Synchronized解析——如果你愿意一层一层剥开我的心

Monitor的工作原理图如下:

线程想要获取monitor, 首先会进入Entry Set队列,它是Waiting Thread,线程状态是Waiting for monitor entry。
当某个线程成功获取对象的monitor后,进入Owner区域,它就是Active Thread。
如果线程调用了wait()方法,则会进入Wait Set队列,它会释放monitor锁,它也是Waiting Thread,线程状态 in Object.wait()
如果其他线程调用 notify() / notifyAll() ,会唤醒Wait Set中的某个线程,该线程再次尝试获取monitor锁,成功即进入Owner区域。

Dump 文件分析关注重点

  • runnable,线程处于执行中
  • deadlock,死锁(重点关注)
  • blocked,线程被阻塞 (重点关注)
  • parked,停止
  • locked,对象加锁
  • waiting,线程正在等待
  • waiting to lock 等待上锁
  • Object.wait(),对象等待中
  • waiting for monitor entry 等待获取监视器(重点关注)
  • waiting on condition,等待资源(重点关注),最常见的情况是线程在等待网络的读写

实战案例1:jstack 分析死锁问题

  • 什么是死锁?
  • 如何用jstack排查死锁?

什么是死锁?

死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法进行下去。

如何用如何用jstack排查死锁问题
先来看一段会产生死锁的Java程序,源码如下:

/**
 * Java 死锁demo
 */
public class DeathLockTest {
    private static Lock lock1 = new ReentrantLock();
    private static Lock lock2 = new ReentrantLock();

    public static void deathLock() {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                try {
                    lock1.lock();
                    System.out.println(Thread.currentThread().getName() + " get the lock1");
                    Thread.sleep(1000);
                    lock2.lock();
                    System.out.println(Thread.currentThread().getName() + " get the lock2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread t2 = new Thread() {
            @Override
            public void run() {
                try {
                    lock2.lock();
                    System.out.println(Thread.currentThread().getName() + " get the lock2");
                    Thread.sleep(1000);
                    lock1.lock();
                    System.out.println(Thread.currentThread().getName() + " get the lock1");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //设置线程名字,方便分析堆栈信息
        t1.setName("mythread-jay");
        t2.setName("mythread-tianluo");
        t1.start();
        t2.start();
    }
    public static void main(String[] args) {
        deathLock();
    }
}

运行结果:

显然,线程jay和线程tianluo都是只执行到一半,就陷入了阻塞等待状态~
jstack排查Java死锁步骤

在终端中输入jsp查看当前运行的java程序
使用 jstack -l pid 查看线程堆栈信息
分析堆栈信息

在终端中输入jps查看当前运行的java程序

通过使用 jps 命令获取需要监控的进程的pid,我们找到了23780 DeathLockTest
使用 jstack -l pid 查看线程堆栈信息

由上图,可以清晰看到死锁信息:

mythread-tianluo 等待这个锁 “0x00000000d61ae3a0”,这个锁是由于mythread-jay线程持有。
mythread-jay线程等待这个锁“0x00000000d61ae3d0”,这个锁是由mythread-tianluo 线程持有。

还原死锁真相

“mythread-tianluo"线程堆栈信息分析如下:

mythread-tianluo的线程处于等待(waiting)状态,持有“0x00000000d61ae3d0”锁,等待“0x00000000d61ae3a0”的锁

“mythread-jay"线程堆栈信息分析如下:

mythread-tianluo的线程处于等待(waiting)状态,持有“0x00000000d61ae3a0”锁,等待“0x00000000d61ae3d0”的锁

实战案例2:jstack 分析CPU过高问题

来个导致CPU过高的demo程序,一个死循环,哈哈~

/**
 * 有个导致CPU过高程序的demo,死循环
 */
public class JstackCase {

     private static ExecutorService executorService = Executors.newFixedThreadPool(5);

    public static void main(String[] args) {

        Task task1 = new Task();
        Task task2 = new Task();
        executorService.execute(task1);
        executorService.execute(task2);
    }

    public static Object lock = new Object();

    static class Task implements Runnable{

        public void run() {
            synchronized (lock){
                long sum = 0L;
                while (true){
                    sum += 1;
                }
            }
        }
    }
}

jstack 分析CPU过高步骤

  1. top
  2. top -Hp pid
  3. jstack pid
  4. jstack -l [PID] >/tmp/log.txt
  5. 分析堆栈信息

1.top
在服务器上,我们可以通过top命令查看各个进程的cpu使用情况,它默认是按cpu使用率由高到低排序的

由上图中,我们可以找出pid为21340的java进程,它占用了最高的cpu资源,凶手就是它,哈哈!

2.top -Hp pid
通过top -Hp 21340可以查看该进程下,各个线程的cpu使用情况,如下:

可以发现pid为21350的线程,CPU资源占用最高,嘻嘻,小本本把它记下来,接下来拿jstack给它拍片子

3. jstack pid
通过top命令定位到cpu占用率较高的线程之后,接着使用jstack pid命令来查看当前java进程的堆栈状态,jstack 21350后,内容如下:

4. jstack -l [PID] >/tmp/log.txt
其实,前3个步骤,堆栈信息已经出来啦。但是一般在生成环境,我们可以把这些堆栈信息打到一个文件里,再回头仔细分析哦~

5. 分析堆栈信息
我们把占用cpu资源较高的线程pid(本例子是21350),将该pid转成16进制的值

在thread dump中,每个线程都有一个nid,我们找到对应的nid(5366),发现一直在跑(24行)

这个时候,可以去检查代码是否有问题啦~ 当然,也建议隔段时间再执行一次stack命令,再一份获取thread dump,毕竟两次拍片结果(jstack)对比,更准确嘛~

### 使用 Jstack 命令查看 JVM 的线程和 GC 情况 Jstack 是一种强大的工具,用于生成 Java 线程的堆栈转储信息。通过这些信息,可以深入了解 JVM 的运行状况以及线程的状态。以下是关于如何使用 Jstack 查看 JVM 的线程和 GC 情况的相关内容。 #### 1. 查看指定进程的线程堆栈信息 可以通过以下命令获取指定 Java 进程的线程堆栈信息: ```bash jstack <pid> ``` 其中 `<pid>` 是目标 Java 进程的进程 ID。此命令将输出当前时间点该进程的所有线程堆栈信息[^1]。 如果需要将堆栈信息保存到文件中以便后续分析,可以使用重定向操作符: ```bash jstack <pid> > thread_dump.txt ``` 这样会将线程堆栈信息写入 `thread_dump.txt` 文件中[^3]。 #### 2. 包含本地方法堆栈信息 默认情况下,Jstack 只显示 Java 层面的堆栈信息。如果希望同时包含本地方法(Native Method)的堆栈信息,可以使用 `-m` 参数: ```bash jstack -m <pid> ``` 此命令会输出包括本地代码在内的完整堆栈信息,这对于分析涉及本地库调用的问题非常有用。 #### 3. 分析线程状态 在生成的线程堆栈信息中,每个线程都会标注其状态(如 RUNNABLE、WAITING、BLOCKED 等)。通过分析这些状态,可以判断是否存在死锁、线程阻塞或其他性能问题。例如,如果发现大量线程处于 BLOCKED 状态,则可能表明存在资源竞争或同步问题[^2]。 #### 4. 查找垃圾回收 (GC) 相关线程 JVM 中的垃圾回收器通常由特定的线程负责执行。在生成的线程堆栈信息中,可以搜索与 GC 相关的线程名称(如 `G1 Main Marker` 或 `Concurrent Mark-Sweep`),以了解 GC 的运行情况。例如: - 如果发现 `G1 Main Marker` 线程频繁运行,可能表明 G1 垃圾回收器正在进行标记阶段。 - 如果看到 `Concurrent Mark-Sweep` 线程活动较多,可能是 CMS 收集器正在清理老年代对象。 #### 5. 定位高 CPU 消耗的线程 如果需要排查 CPU 高消耗问题,可以结合操作系统工具(如 `top` 或 `jps`)找到对应的 Java 进程 ID,然后使用 Jstack 获取线程堆栈信息。通过分析堆栈信息中的热点方法,可以定位导致 CPU 占用高的具体代码位置[^2]。 --- ### 示例代码 以下是一个简单的脚本,用于自动获取并保存线程堆栈信息: ```bash #!/bin/bash PID=$1 if [ -z "$PID" ]; then echo "Usage: $0 <pid>" exit 1 fi jstack $PID > thread_dump_$PID_$(date +%Y%m%d%H%M%S).txt echo "Thread dump saved to thread_dump_$PID_$(date +%Y%m%d%H%M%S).txt" ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值