• JVM——jstack命令


    概述

    jstack是JVM自带的Java堆栈跟踪工具,它用于打印出给定的java进程ID、core file、远程调试服务的Java堆栈信息,它可以非常方便的做java进程的thread dump。

     

    一、jstack 介绍

    jstack 功能

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

    jstack 用法

    [root@push ~]# jstack -help
    Usage:
        jstack [-l] <pid>
            (to connect to running process)
        jstack -F [-m] [-l] <pid>
            (to connect to a hung process)
        jstack [-m] [-l] <executable> <core>
            (to connect to a core file)
        jstack [-m] [-l] [server_id@]<remote server IP or hostname>
            (to connect to a remote debug server)
    
    Options:
        -F  to force a thread dump. Use when jstack <pid> 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
    • -F: 当正常输出的请求不被响应时,强制输出线程堆栈
    • -m: 如果调用到本地方法的话,可以显示C/C++的堆栈
    • -l: 除堆栈外,显示关于锁的附加信息,在发生死锁时可以用jstack -l pid来观察锁持有情况
    • -h: 打印帮助信息

    示例一:no option

    命令:jstack <pid>

    描述:打印堆栈信息在控制台。

    示例二:no option

    命令:jstack <pid> > <pid>_core.dump

    描述:打印堆栈信息并输出到文件。

    示例三:-F

    命令:jstack -F <pid>

    描述:当进程挂起(hung)时,上面的命令可能没有响应,这时需要使用 "-F" 参数来强制执行thread dump。

    示例四:-l

    命令:jstack -l <pid>

    描述:显示关于锁的附加信息,在发生死锁时可以用jstack -l pid来观察锁持有情况。

    二、线程状态等基础回顾

    线程状态简介

    jstack用于生成线程快照的,我们分析线程的情况,需要复习一下线程状态。

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

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

    Monitor 监视锁

    因为Java程序一般都是多线程运行的,Java多线程跟监视锁环环相扣,所以我们分析线程状态时,也需要回顾一下Monitor监视锁知识。

    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排查死锁?

    1、什么是死锁?

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

    2、如何用如何用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都是只执行到一半,就陷入了阻塞等待状态~

    3、jstack排查Java死锁步骤

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

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

    通过使用 jps 命令获取需要监控的进程的pid,我们找到了23780 DeathLockTest

    3.2、使用 jstack -l pid 查看线程堆栈信息

    由上图,可以清晰看到死锁信息:
    • mythread-tianluo 等待这个锁 “0x00000000d61ae3a0”,这个锁是由于mythread-jay线程持有。
    • mythread-jay线程等待这个锁“0x00000000d61ae3d0”,这个锁是由mythread-tianluo 线程持有。

    3.3、还原死锁真相

    “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. pid转换成16进制值

    我们把占用cpu资源较高的线程pid(本例子是21350),将该pid转成16进制的值。

    [root@push ~]# printf '%x
    ' 21350
    5366

    4. jstack pid

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

    jstack 21350

    也可以用以下命令直接过滤:

    jstack 21350 | grep 5366

    5. jstack -l [PID] >/tmp/log.txt

    其实,前3个步骤,堆栈信息已经出来啦。但是一般在生成环境,我们可以把这些堆栈信息打到一个文件里,再回头仔细分析哦~

    6. 分析堆栈信息

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

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

    引用:

  • 相关阅读:
    题解 nflsoj204 排列
    题解 CF1328 D,E,F Carousel, Tree Queries, Make k Equal
    题解 LOJ3277 「JOISC 2020 Day3」星座 3
    题解 nflsoj464 CF1267K 正睿1225:一个简单的计数技巧
    题解 CF1326F2 Wise Men (Hard Version)
    题解 CF1326E Bombs
    题解 CF1316E and UOJ495:一类结合贪心的背包问题
    仓鼠的DP课 学习笔记
    题解 CF1314B Double Elimination
    固件
  • 原文地址:https://www.cnblogs.com/caoweixiong/p/14785410.html
Copyright © 2020-2023  润新知