jstack笔记

遇到java程序跑不动怎么办,jstack是比较容易想到的一个工具,利用jstack来dump出一个线程堆栈快照,而后具体分析。html

通常的堆栈大概是由下面的部分组成的:java

 1 "resin-22129" daemon prio=10 tid=0x00007fbe5c34e000 nid=0x4cb1 waiting on condition [0x00007fbe4ff7c000]
 2    java.lang.Thread.State: WAITING (parking)
 3     at sun.misc.Unsafe.park(Native Method)
 4     at java.util.concurrent.locks.LockSupport.park(LockSupport.java:315)
 5     at com.caucho.env.thread2.ResinThread2.park(ResinThread2.java:196)
 6     at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:147)
 7     at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118)
 8 
 9 
10 "Timer-20" daemon prio=10 tid=0x00007fe3a4bfb800 nid=0x1a31 in Object.wait() [0x00007fe3a077a000]
11    java.lang.Thread.State: TIMED_WAITING (on object monitor)
12     at java.lang.Object.wait(Native Method)
13     - waiting on <0x00000006f0620ff0> (a java.util.TaskQueue)
14     at java.util.TimerThread.mainLoop(Timer.java:552)
15     - locked <0x00000006f0620ff0> (a java.util.TaskQueue)
16     at java.util.TimerThread.run(Timer.java:505)
  • "resin-22129" 线程名称:若是使用 java.lang.Thread 类生成一个线程的时候,线程名称为 Thread-(数字) 的形式,这里是resin生成的线程;
  • daemon 线程类型:线程分为守护线程 (daemon) 和非守护线程 (non-daemon) 两种,一般都是守护线程;
  • prio=10 线程优先级:默认为5,数字越大优先级越高;
  • tid=0x00007fbe5c34e000 JVM线程的id:JVM内部线程的惟一标识,经过 java.lang.Thread.getId()获取,一般用自增的方式实现;
  • nid=0x4cb1 系统线程id:对应的系统线程id(Native Thread ID),能够经过 top 命令进行查看,现场id是十六进制的形式;
  • waiting on condition 系统线程状态:这里是系统的线程状态;
  • [0x00007fbe4ff7c000] 起始栈地址:线程堆栈调用的其实内存地址;
  • java.lang.Thread.State: WAITING (parking) JVM线程状态:这里标明了线程在代码级别的状态。
  • 线程调用栈信息:下面就是当前线程调用的详细栈信息,用于代码的分析。堆栈信息应该从下向上解读,由于程序调用的顺序是从下向上的。

系统线程状态 (Native Thread Status)

系统线程有以下状态:数据库

deadlock

死锁线程,通常指多个线程调用期间进入了相互资源占用,致使一直等待没法释放的状况。api

runnable

通常指该线程正在执行状态中,该线程占用了资源,正在处理某个操做,如经过SQL语句查询数据库、对某个文件进行写入等。网络

blocked

线程正处于阻塞状态,指当前线程执行过程当中,所须要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,能够理解为等待资源超时的线程。多线程

waiting on condition

线程正处于等待资源或等待某个条件的发生,具体的缘由须要结合下面堆栈信息进行分析。oracle

(1)若是堆栈信息明确是应用代码,则证实该线程正在等待资源,通常是大量读取某种资源且该资源采用了资源锁的状况下,线程进入等待状态,等待资源的读取,或者正在等待其余线程的执行等。app

(2)若是发现有大量的线程都正处于这种状态,而且堆栈信息中得知正等待网络读写,这是由于网络阻塞致使线程没法执行,颇有多是一个网络瓶颈的征兆:工具

  • 网络很是繁忙,几乎消耗了全部的带宽,仍然有大量数据等待网络读写;
  • 网络多是空闲的,但因为路由或防火墙等缘由,致使包没法正常到达;

因此必定要结合系统的一些性能观察工具进行综合分析,好比netstat统计单位时间的发送包的数量,看是否很明显超过了所在网络带宽的限制;观察CPU的利用率,看系统态的CPU时间是否明显大于用户态的CPU时间。这些都指向因为网络带宽所限致使的网络瓶颈。oop

(3)还有一种常见的状况是该线程在 sleep,等待 sleep 的时间到了,将被唤醒。

waiting for monitor entry 或 in Object.wait()

Moniter 是Java中用以实现线程之间的互斥与协做的主要手段,它能够当作是对象或者class的锁,每一个对象都有,也仅有一个 Monitor。

从上图能够看出,每一个Monitor在某个时刻只能被一个线程拥有,该线程就是 "Active Thread",而其余线程都是 "Waiting Thread",分别在两个队列 "Entry Set"和"Waint Set"里面等待。其中在 "Entry Set" 中等待的线程状态是 waiting for monitor entry,在 "Wait Set" 中等待的线程状态是 in Object.wait()

(1)"Entry Set"里面的线程。
咱们称被 synchronized 保护起来的代码段为临界区

当一个线程申请进入临界区时,它就进入了 "Entry Set" 队列中,这时候有两种可能性:

  • 该Monitor不被其余线程拥有,"Entry Set"里面也没有其余等待的线程。本线程即成为相应类或者对象的Monitor的Owner,执行临界区里面的代码;此时在Thread Dump中显示线程处于 "Runnable" 状态。
  • 该Monitor被其余线程拥有,本线程在 "Entry Set" 队列中等待。此时在Thread Dump中显示线程处于 "waiting for monity entry" 状态。

临界区的设置是为了保证其内部的代码执行的原子性和完整性,但由于临界区在任什么时候间只容许线程串行经过,这和咱们使用多线程的初衷是相反的。若是在多线程程序中大量使用synchronized,或者不适当的使用它,会形成大量线程在临界区的入口等待,形成系统的性能大幅降低。若是在Thread Dump中发现这个状况,应该审视源码并对其进行改进。

(2)"Wait Set"里面的线程
当线程得到了Monitor,进入了临界区以后,若是发现线程继续运行的条件没有知足,它则调用对象(一般是被synchronized的对象)的wait()方法,放弃Monitor,进入 "Wait Set"队列。只有当别的线程在该对象上调用了 notify()或者notifyAll()方法,"Wait Set"队列中的线程才获得机会去竞争,可是只有一个线程得到对象的Monitor,恢复到运行态。"Wait Set"中的线程在Thread Dump中显示的状态为 in Object.wait()。一般来讲,

一般来讲,当CPU很忙的时候关注 Runnable 状态的线程,反之则关注 waiting for monitor entry 状态的线程。

JVM线程运行状态 (JVM Thread Status)

在 java.lang.Thread.State 中定义了线程的状态:

NEW

至今还没有启动的线程的状态。线程刚被建立,但还没有启动。

RUNNABLE

可运行线程的线程状态。线程正在JVM中执行,有可能在等待操做系统中的其余资源,好比处理器。

BLOCKED

受阻塞而且正在等待监视器的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 以后再次进入同步的块/方法。
在Thread Dump日志中一般显示为 java.lang.Thread.State: BLOCKED (on object monitor) 。

WAITING

某一等待线程的线程状态。线程正在无期限地等待另外一个线程来执行某一个特定的操做,线程由于调用下面的方法之一而处于等待状态:

  • 不带超时的 Object.wait 方法,日志中显示为 java.lang.Thread.State: WAITING (on object monitor)
  • 不带超时的 Thread.join 方法
  • LockSupport.park 方法,日志中显示为 java.lang.Thread.State: WAITING (parking)

TIMED_WAITING

指定了等待时间的某一等待线程的线程状态。线程正在等待另外一个线程来执行某一个特定的操做,并设定了指定等待的时间,线程由于调用下面的方法之一而处于定时等待状态:

  • Thread.sleep 方法
  • 指定超时值的 Object.wait 方法
  • 指定超时值的 Thread.join 方法
  • LockSupport.parkNanos
  • LockSupport.parkUntil

TERMINATED

线程处于终止状态。

根据Java Doc中的说明,在给定的时间上,一个只能处于上述的一种状态之中,而且这些状态都是JVM的状态,跟操做系统中的线程状态无关。

 

参考

https://go-on.iteye.com/blog/1673894

http://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html

https://www.javatang.com/archives/2017/10/25/36441958.html

https://www.cubrid.org/blog/the-principles-of-java-application-performance-tuning

相关文章
相关标签/搜索