如何使用jstack分析线程状态

作者: 美团Java | 来源:发表于2016-12-02 20:02 被阅读15525次

    转载请注明原创出处,谢谢!
    简书占小狼
    http://www.jianshu.com/users/90ab66c248e6/latest_articles

    背景

    记得前段时间,同事说他们测试环境的服务器cpu使用率一直处于100%,本地又没有什么接口调用,为什么会这样?cpu使用率居高不下,自然是有某些线程一直占用着cpu资源,那又如何查看占用cpu较高的线程?

    当然一个正常的程序员不会写出上述代码,这里只是为了让一个线程占用较高的cpu资源。

    top命令

    在linux环境下,可以通过top命令查看各个进程的cpu使用情况,默认按cpu使用率排序

    1、上图中可以看出pid为23344的java进程占用了较多的cpu资源;
    2、通过top -Hp 23344可以查看该进程下各个线程的cpu使用情况;

    上图中可以看出pid为25077的线程占了较多的cpu资源,利用jstack命令可以继续查看该线程当前的堆栈状态。

    jstack命令

    通过top命令定位到cpu占用率较高的线程之后,继续使用jstack pid命令查看当前java进程的堆栈状态

    jstack命令生成的thread dump信息包含了JVM中所有存活的线程,为了分析指定线程,必须找出对应线程的调用栈,应该如何找?

    在top命令中,已经获取到了占用cpu资源较高的线程pid,将该pid转成16进制的值,在thread dump中每个线程都有一个nid,找到对应的nid即可;隔段时间再执行一次stack命令获取thread dump,区分两份dump是否有差别,在nid=0x246c的线程调用栈中,发现该线程一直在执行JstackCase类第33行的calculate方法,得到这个信息,就可以检查对应的代码是否有问题。

    通过thread dump分析线程状态

    除了上述的分析,大多数情况下会基于thead dump分析当前各个线程的运行情况,如是否存在死锁、是否存在一个线程长时间持有锁不放等等。

    在dump中,线程一般存在如下几种状态:
    1、RUNNABLE,线程处于执行中
    2、BLOCKED,线程被阻塞
    3、WAITING,线程正在等待

    实例1:多线程竞争synchronized锁

    很明显:线程1获取到锁,处于RUNNABLE状态,线程2处于BLOCK状态
    1、locked <0x000000076bf62208>说明线程1对地址为0x000000076bf62208对象进行了加锁;
    2、waiting to lock <0x000000076bf62208> 说明线程2在等待地址为0x000000076bf62208对象上的锁;
    3、waiting for monitor entry [0x000000001e21f000]说明线程1是通过synchronized关键字进入了监视器的临界区,并处于"Entry Set"队列,等待monitor,具体实现可以参考深入分析synchronized的JVM实现

    实例2:通过wait挂起线程
    static class Task implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                try {
                    lock.wait();
                    //TimeUnit.SECONDS.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    dump结果

    线程1和2都处于WAITING状态
    1、线程1和2都是先locked <0x000000076bf62500>,再waiting on <0x000000076bf62500>,之所以先锁再等同一个对象,是因为wait方法需要先通过synchronized获得该地址对象的monitor;
    2、waiting on <0x000000076bf62500>说明线程执行了wait方法之后,释放了monitor,进入到"Wait Set"队列,等待其它线程执行地址为0x000000076bf62500对象的notify方法,并唤醒自己,具体实现可以参考深入分析Object.wait/notify实现机制


    我是占小狼
    坐标魔都,白天上班族,晚上是知识的分享者
    如果读完觉得有收获的话,欢迎点赞加关注


    相关文章

      网友评论

      • 绿皮多驱小火车:写的清晰明了,正式我想读的内容,点赞
      • 酱油和醋:“在thread dump中每个线程都有一个nid,找到对应的nid即可”这个怎么找?
      • Unbelievableok:应该是线程2通过synchronized关键字进入了监视器的临界区,并处于"Entry Set"队列,等待monitor
        9b70dfab793e:正想提出了:grin:
      • 北京程序猿:在实际工作中,我也是这么搞的☺
        美团Java:@JasonQiang :+1:
      • 4e7083d856f1:喜欢原理。觉得文章很好,初来关注,请继续加油
        美团Java:@MrmiaoCoder 谢谢支持
      • a421d39aa6fc:请问大侠,有没有遇到过jstack pid没输出结果的情况啊,需要jstack -F。这种是怎么造成的,该如何排查呢?
        文曜繁星:这个种情况,必定是执行用户的问题。通常情况下,都是集成的CI,CD系统,使用对应的用户进行应用的发布。
        a4b0d7dda60d:应该是用户问题,如果程序是用proc用户起来的,就用proc用户去执行jstack,连root用户都不行。
      • 此木林栖:理解了synchronized和wait,栈状态就显而易见了
        美团Java:@此木林栖 对的,我有两篇文章讲锁和wait的
      • kingrush:之前也试过这种方法,遇到过top中的pid转16进制后,在dump文件中找不到对应的线程
        f1ed3b668569:不知道例子中的pid转16进制 怎么转的,pid为25077 对应 nid=0x246c 。
        酱油和醋:我也是
      • 简书闪电侠:metty是什么鬼
        美团Java:@the_flash 哈哈,这是情结

      本文标题:如何使用jstack分析线程状态

      本文链接:https://www.haomeiwen.com/subject/dimxmttx.html