线上高并发时,tomcat挂掉了,应用假死等问题,都可以使用jstack查看线程堆栈的问题。
jstack参数解说
首先线程状态如下:
New: 当线程对象创建时存在的状态,此时线程不可能执行;
Runnable:当调用thread.start()后,线程变成为Runnable状态。只要得到CPU,就可以执行;
Running:线程正在执行;
Waiting:执行thread.join()或在锁对象调用obj.wait()等情况就会进该状态,表明线程正处于等待某个资源或条件发生来唤醒自己;
Timed_Waiting:执行Thread.sleep(long)、thread.join(long)或obj.wait(long)等就会进该状态,与Waiting的区别在于Timed_Waiting的等待有时间限制;
Blocked:如果进入同步方法或同步代码块,没有获取到锁,则会进入该状态;
Dead:线程执行完毕,或者抛出了未捕获的异常之后,会进入dead状态,表示该线程结束
其次,对于jstack日志,我们要着重关注如下关键信息
Deadlock:表示有死锁
Waiting on condition:等待某个资源或条件发生来唤醒自己。比如线程正在sleep,网络读写繁忙而等待.
此时线程状态大致为以下几种:
java.lang.Thread.State: WAITING (parking):一直等那个条件发生;
java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。
Blocked:阻塞
Waiting on monitor entry:在等待获取锁,意味着它在等待进入一个临界区,所以它在”Entry Set“队列中等待,等待获取监视器。
in Object.wait():获取锁后又执行obj.wait()放弃锁,进入等待队列中。
sleeping:休眠的线程,调用了Thread.sleep()。
runnable:正在运行中。
注意: jstack表示的进程当前的状态哦。如果比如看到状态位RUNNABLE的,表示在你执行jstack时,该线程正在运行中。
对于Waiting on monitor entry 和 in Object.wait()的详细描述:Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。从下图中可以看出,每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 "Running Thread",而其它线程都是 "Waiting Thread",分别在两个队列 " Entry Set"和 "Wait Set"里面等候。
在 "Entry Set"中等待的线程状态是:"Waiting for monitor entry",等待获取锁,在排队呢。
而在 "Wait Set"中等待的线程状态是 "in Object.wait()" 获取了锁,但是执行了wait()操作,线程被挂起,等待被唤醒,同样得重新获取锁。如下图的流转
大量某个状态
如果大量线程在“waiting for monitor entry”:
可能是一个全局锁阻塞住了大量线程。如果短时间内打印的thread dump文件反映,随着时间流逝,waiting for monitor entry 的线程越来越多,没有减少的趋势,可能意味着某些线程在临界区里呆的时间太长了,以至于越来越多新线程迟迟无法进入临界区。
如果大量线程在“waiting on condition”:
可能是获取第三方资源,尤其是第三方网络资源,迟迟获取不到Response,导致大量线程进入等待状态。
所以如果你发现有大量的线程都处在Wait on condition,从线程堆栈看,正等待网络读写,这可能是一个网络瓶颈的征兆,因为网络阻塞导致线程无法执行。
问题状态
Deadlock:表示有死锁
Waiting on condition:等待某个资源或条件发生来唤醒自己。具体需要结合jstacktrace来分析,比如线程正在sleep,网络读写繁忙而等待
Blocked:阻塞
Waiting on monitor entry:在等待获取锁
虽然状态是这么多,但是具体问题还的具体查看,比如等待唤醒的资源,在下一次jstack查看时不存在了,就说明已经获取到锁并执行了。如果存在大量的,且长时间阻塞的那么就说明有很大问题了。
jstack其他参数说明
prio:线程的优先级
tid:线程id
nid:操作系统映射的线程id(16进制)
[0x0000000000000000] :后面末尾的数字,表示线程栈的起始地址。
基础操作
//查找进程号
> jps -lv |grep '应用名称' 或者ps -ef|grep '应用名称'
> jstack 3333 //进程号
top找到堆栈信息
> top //进入top后,shift+H查看线程
找到占用内存较大的线程id
> printf "%x\n" TID 将需要的线程ID转换为16进制格式 (16转10: printf %d\\n 0x5b7f)
> jstack [进程]|grep -A 10 [线程的16进制] //根据jstack找到该线程占用比较大的堆栈信息。
> jstack 13333 |grep -A 10 54f2
其他命令:
输出该进程拥有的线程总数
ps -mp 5841 -o THREAD,tid,time | wc -l
输出占用cpu最大的前10线程
ps -mp 5841 -o THREAD,tid,time | sort -rn | head -10
参考地址