作为一名搞技术的程序猿或者是攻城狮,想必你应该是对下面这两个问题有所了解,说不定你在实际的工作或者面试就有遇到过:
第一个问题:Java死锁如何排查和解决?
第二个问题:服务器CPU占用率高达到100%排查和解决?
第三个问题:有哪些工具能够快速查看线程使用情况?
本文对这三个问题进行总结整理,通过实例演示讲解,精彩干货,不容错过啊!
前戏就这么多,高潮会很多,做好了,让我们直奔主题,发动小船,Let's go!
要排查和解决死锁,首先思考三个问题:
1. 什么是死锁?
2. 为什么会出现死锁?
3. 怎么排查代码中出现了死锁?
4. 如何避免写出死锁的代码?
作为技术人员(工程师),在出现问题的时候,能够尽快的去解决这个问题。但是在学习技术知识的时候,还是脚踏实地,多问一些为什么,一个好的问题,能够让自己思考,这方面的能力也一定要锻炼锻炼哦,这样才能更好的理解和掌握知识,并探究/触碰到更深入的地方。
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。[百度百科:死锁]
注:进程和线程都可以发生死锁,只要满足死锁的条件!
从上面的概念中我们知道
(1)必须是两个或者两个以上进程(线程)
(2)必须有竞争资源
首先整一个死锁的代码,看例子:
上面这段代码执行后,就会出现死锁,排查的姿势有如下几种,搞起来吧!
一:在windons命令窗口,使用jps -l
【不会使用jps请自行查询资料】
<img0.20432432432432432" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExAhZ3pickyqyadQZxZR1clLCFAuZj2Xia9dDpvQk9xicfpMkgM3EuqcpSQ/640?wx_fmt=png" data-type="png" data-w="925" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExAhZ3pickyqyadQZxZR1clLCFAuZj2Xia9dDpvQk9xicfpMkgM3EuqcpSQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.20432432432432432">
二:使用 jstack -l 12316
【不会使用jstack请自行查询资料】
在window打开 JConsole,JConsole是一个图形化的监控工具!
一:在windons命令窗口 ,输出JConsole
,如下图:
二:选择到线程的tab上,如下截图。
<img0.8861386138613861" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExAdTriajoCwHZHBqJZO03CFg6nAFn5mEibMDzx8IBwMsp1HxSNBx64kuw/640?wx_fmt=png" data-type="png" data-w="808" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExAdTriajoCwHZHBqJZO03CFg6nAFn5mEibMDzx8IBwMsp1HxSNBx64kuw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.8861386138613861">
在window打开 jvisualvm,jvisualvm是一个图形化的监控工具!
一:在windons命令窗口 ,输出 jvisualvm
<img0.39355322338830584" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExHDPNVLERpGDgIMMRNRqOQBEBYZHDoWhrV5wtxR6vicZUibPPeCFergicA/640?wx_fmt=png" data-type="png" data-w="1334" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExHDPNVLERpGDgIMMRNRqOQBEBYZHDoWhrV5wtxR6vicZUibPPeCFergicA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.39355322338830584">
二:依然是切换到线程这个TAB上,很明显的就有提示!
<img0.33914421553090335" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExFBhz4gGtsDEf8xN0r0G1ffUa91bPq626a949ZSyTQpuvCHMILMr9tA/640?wx_fmt=png" data-type="png" data-w="1893" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExFBhz4gGtsDEf8xN0r0G1ffUa91bPq626a949ZSyTQpuvCHMILMr9tA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.33914421553090335">
上面说了死锁出现的原因以及通过三种方式来检测和排查死锁,下面更重要的东西来了,就是如何避免死锁,如果能够让写出的代码避免死锁出现也就没有上面这些排查的过程了。最好的是从源头控制问题,而不是后期遇到问题在去填坑。
我看了阿里巴巴中最新的开发规约,里面有对避免死锁的说明,具体如下:
【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会 造成死锁。说明:线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是 A、B、C,否则可能出现死锁。
平时多积累一点,这样在遇到问题的时候就少句求人的话。如果在实际的开发中遇到CPU 100%问题,要怎么排查呢?如果你没有遇到过这个问题,请先自己思考10s,如果你遇到过,这个时候也正好可以在回顾一遍。
一、 使用top
命令查看cpu占用资源较高的PID
<img0.41654135338345866" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExTibAicUj7UI5GG12SLVeuO2n3cDh9l6IYFvwEhoG9rw54bXaVgAc5Vng/640?wx_fmt=png" data-type="png" data-w="665" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExTibAicUj7UI5GG12SLVeuO2n3cDh9l6IYFvwEhoG9rw54bXaVgAc5Vng/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.41654135338345866">
当前占用cup100% 的PID为3455。
二、通过jps
找到当前用户下的java程序PID
执行jps -l
能够打印出所有的应用的PID,找到有一个PID和这个cpu使用100%一样的ID!!!就知道是哪一个服务了。知道了对应的服务,在接着后续的分析步骤。
三、 使用 pidstat -p < PID > 1 3 -u -t
-p:指定进程号
-u:默认的参数,显示各个进程的cpu使用统计
-t:显示选择任务的线程的统计信息外的额外信息
<img0.036057692307692304" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DEx7TtJ9F10Jv4Pcwr4CicHQeL4ZaJ7f8mm6DmkNR2spk9BXxic5QDAek3Q/640?wx_fmt=png" data-type="png" data-w="832" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DEx7TtJ9F10Jv4Pcwr4CicHQeL4ZaJ7f8mm6DmkNR2spk9BXxic5QDAek3Q/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"><img0.41229193341869397" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExUkBzUicCiavhiaSqv6RLXK9b2CmiaP2lE5diaZH3gEeDQqulkHRyhkUZJzw/640?wx_fmt=png" data-type="png" data-w="781" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExUkBzUicCiavhiaSqv6RLXK9b2CmiaP2lE5diaZH3gEeDQqulkHRyhkUZJzw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.41229193341869397"></img0.036057692307692304">
四、找到cpu占用较高的线程TID ,通过上图发现是 3467
的TID占用cup较大
五、 因为jstack命令输出文件记录的线程ID是16进制。因此我们先将TID转换为十六进制的表示方式,转换方式可以参考下图。
将3467
转为十六进制 d8d
,注意是小写!! 记录下来,后面会使用。
六、通过jstack [-l] PID
输出当前进程的线程信息
jstack PID /temp/test.log
七、查找 TID对应的线程(输出的线程id为十六进制),找到对应的代码,使用命令查找哦,不要肉眼比对,具体命令请思考,给你表现机会。
找到之后具体分析这个线程在干什么,为什么会占用这么多的 CUP资源。<img0.4216133942161339" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExBIa74ZWbntMfTmtFo9dpHEc5uqklbD6IARx86zsia0YmYibh9rPR4Zkw/640?wx_fmt=png" data-type="png" data-w="657" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExBIa74ZWbntMfTmtFo9dpHEc5uqklbD6IARx86zsia0YmYibh9rPR4Zkw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.4216133942161339">
PS:线程的几种状态如下说明:
NEW,未启动的。不会出现在Dump中。
RUNNABLE,在虚拟机内执行的。
BLOCKED,受阻塞并等待监视器锁。
WATING,无限期等待另一个线程执行特定操作。
TIMED_WATING,有时限的等待另一个线程的特定操作。
TERMINATED,已退出的。
此处省略......,好多字。
一 :show-busy-java-threads
官网地址:show-busy-java-threads[1]:https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads<img0.5328836424957841" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExBPQpyqvJHfP4OM0rb9JoQPryB9BPKm4mV09GPy0FcmrH1MVazjK9IQ/640?wx_fmt=png" data-type="png" data-w="1186" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExBPQpyqvJHfP4OM0rb9JoQPryB9BPKm4mV09GPy0FcmrH1MVazjK9IQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.5328836424957841">
简单安装和使用过程:
1.下载 show-busy-java-threads[2]2.上传服务器,然后进行解压3.然后执行对应的命令
<img0.7906458797327395" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExibXgR6szVQ3Mb6YnR98pd19wPIWWxJ600icW3RNfYVu3dkKibfa2FdbicA/640?wx_fmt=png" data-type="png" data-w="898" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExibXgR6szVQ3Mb6YnR98pd19wPIWWxJ600icW3RNfYVu3dkKibfa2FdbicA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.7906458797327395">
二:阿里开源的问题定位神器 arthas
来定位问题。
官网地址:arthas[3] :https://alibaba.github.io/arthas/index.html
这个里面有很多命令,如thread
支持一键展示当前最忙的前N个线程并打印堆栈,最简单的 thread -n 10
即可将最忙碌的十个线程快照打印出来,真正高效。
<img0.7958333333333333" data-src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExdZicW2NR1RRiaMJJpqqN6SDib8Kyzgqib78rkqCaggqh2h5ibhv5vZ8UOVA/640?wx_fmt=png" data-type="png" data-w="960" title="null" _width="315px" src="https://mmbiz.qpic.cn/mmbiz_png/yziabN7pK2YgTnbTywosjClia4ba6T4DExdZicW2NR1RRiaMJJpqqN6SDib8Kyzgqib78rkqCaggqh2h5ibhv5vZ8UOVA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" crossorigin="anonymous" data-fail="0" style="margin: 20px auto; max-width: 100%; line-height: 1.5; border-radius: 4px; display: block; overflow-wrap: break-word !important; height: auto !important; width: 315px !important; visibility: visible !important;"></img0.7958333333333333">
定位神器 arthas
安装过程就做介绍了,如果你还没有用过这个工具,我建议一定去用一下,说不定你会爱上它!
本文内容比较多,基本上是手把手的教程了,希望能够对你有所帮助,也建议没有遇到类似问题的伙伴,看完之后一定要亲自去实践一下操作过程(有些姿势光看不用是不行的),如果没有环境可以自行想办法搞一个测试例子。还是老话:不要眼高手低,看了和做了本质上两个概念,最终收获的也一定不同。
也可以通过使用jstack
找到系统的代码性能问题
1、在进行压力测试的时候,使用jps找到应用的PID
2、然后使用jstack
输出出压力测试时候应用的dump信息
3、分析输出的日志文件中那个方法block
线程占用最多,这里可能是性能有问题,找到对应的代码分析