堆栈信息分析

摘要:
新:线程是由用户创建的,尚未被设置处理。RUNNABLE:线程占用CPU并处理任务。BLOCKED:线程正在等待另一个线程释放其锁以获取监视器锁。WAITING:线程正在使用wait、join或ark方法等待。TIMED_WAITING:线程正在使用sleep、wait、join或park方法等待。2、 死锁3.在值得注意的对象转储文件中,值得注意的线程状态包括:1.死锁,死锁2.正在运行,可运行3.等待资源,等待条件4.等待获取监视器,等待监视器入口5.暂停,暂停6.对象等待,对象等待()或TIMED_WAITING7,阻塞,阻止的综合演示1:Waittolock和Blocked实例如下:“RMITCPConnection-1772.16.5.25”daemonprio=10tid=0x00007fd508371000nid=0x55aewaitingformmonitorentry[0x0007fd4f8684000]java.lang.Thread.State:BLOCKEDatorg.apache.log4j.Category.callAppenders waiting to lockatorg.apache.log4j.Category.forcedLogtorg.apache.log2j.Category.logtorg.apache.commons.loging.impl.Log4JLogger.warnatcom.tuan.core.common.lang.cache.remote.PyMemcachedClient。获取……1)线程状态为“已阻止”。2) “Waiting to lock”表示线程正在等待锁定0x00000000acf4d0地址。3) 在转储日志中查找字符串0x00000000acf4d0c0,发现大量线程正在等待锁定此地址。如果你能在日志中找到谁获得了锁,你就可以从中吸取教训。

一、堆栈信息解读

2013-01-13 11:02:31
Full thread dump Java HotSpot(TM) Client VM (23.1-b03 mixed mode, sharing):
"[ThreadPool Manager] - Idle Thread" daemon prio=6 tid=0x069a3400 nid=0x84 in Object.wait() [0x0795f000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x239102b8> (a org.eclipse.equinox.internal.util.impl.tpt.threadpool.Executor)
    at java.lang.Object.wait(Object.java:503)
    at org.eclipse.equinox.internal.util.impl.tpt.threadpool.Executor.run(Executor.java:106)
    - locked <0x239102b8> (a org.eclipse.equinox.internal.util.impl.tpt.threadpool.Executor)
    Locked ownable synchronizers:
    - None
1、这个线程的名称为[ThreadPool Manager] - Idle Thread,实际开发过程中使用可以使用Thread对象的setName方法修改;这里可以看出设置线程名字属性的必要性,导出栈信息的时候就有用了,可以通过线程名字方便的定位到问题代码块;
2、优先级为6,不过优先级意义不大,对分析应用帮助不大,Java不推荐修改线程的优先级,所以这里跳过;
3、tid和nid,16进制表示,这两个字段中有一个可以对应到操作系统调度时使用的线程ID,这在Linux下特别有用,因为top -H命令可以输出各个线程的CPU占用率,借助栈信息,可以方便的定位到CPU占用高的线程,这个很有用,在其它平台暂时还看不出什么意义;
4、当前栈顶,为java.lang.Object.wait方法;
5、线程状态,java.lang.Thread.State: WAITING (on object monitor);java.lang.Thread.State是什么?有兴趣的话可以翻看JDK的源码,源码很简单,注释很详细,给出了Java线程可能存在的状态;
  • NEW,线程对象被new出来,但是还没有调用start方法;
  • RUNNABLE,线程对象已经被调用了start方法;
  • BLOCKED,简单的说,线程进入了synchronized关键字标识的同步块,但和4状态有所区别;
  • WAITING和TIMED_WAITING差不多,一般是调用了对象的wait方法,需要其它线程在特定场景使用notify/notifyAll方法;
  • TERMINATED,这个状态比较直白,run方法退出运行,即进入这个状态
6、当前线程是否处于同步块内,   Locked ownable synchronizers:,这里值为None,说明当前线程没有处于同步块内。
堆栈信息分析第1张
  • NEW: The thread is created but has not been processed yet.
  • RUNNABLE: The thread is occupying the CPU and processing a task. (It may be in WAITING status due to the OS's resource distribution.)
  • BLOCKED: The thread is waiting for a different thread to release its lock in order to get the monitor lock.
  • WAITING: The thread is waiting by using a wait, join or park method.
  • TIMED_WAITING: The thread is waiting by using a sleep, wait, join or park method. (The difference from WAITING is that the maximum waiting time is specified by the method parameter, and WAITING can be relieved by time as well as external changes.)

 二、死锁 

堆栈信息分析第2张

三、值得关注的对象

Dump文件里,值得关注的线程状态有:

1、死锁,Deadlock(重点关注) 

2、执行中,Runnable   

3、等待资源,Waiting on condition(重点关注) 

4、等待获取监视器,Waiting on monitor entry(重点关注)

5、暂停,Suspended

6、对象等待中,Object.wait()  或 TIMED_WAITING

7、阻塞,Blocked(重点关注)

综合示范一:Waiting to lock 和 Blocked
实例如下:
"RMI TCP Connection(267865)-172.16.5.25" daemon prio=10 tid=0x00007fd508371000 nid=0x55ae waiting for monitor entry [0x00007fd4f8684000]
   java.lang.Thread.State: BLOCKED (on object monitor)
  at org.apache.log4j.Category.callAppenders(Category.java:201)
  - waiting to lock <0x00000000acf4d0c0> (a org.apache.log4j.Logger)
  at org.apache.log4j.Category.forcedLog(Category.java:388)
  at org.apache.log4j.Category.log(Category.java:853)
  at org.apache.commons.logging.impl.Log4JLogger.warn(Log4JLogger.java:234)
  at com.tuan.core.common.lang.cache.remote.SpyMemcachedClient.get(SpyMemcachedClient.java:110)
  ……
1)线程状态是 Blocked,阻塞状态。说明线程等待资源超时!
2)“ waiting to lock <0x00000000acf4d0c0>”指,线程在等待给这个 0x00000000acf4d0c0 地址上锁(英文可描述为:trying to obtain  0x00000000acf4d0c0 lock)。
3)在 dump 日志里查找字符串 0x00000000acf4d0c0,发现有大量线程都在等待给这个地址上锁。如果能在日志里找到谁获得了这个锁(如locked < 0x00000000acf4d0c0 >),就可以顺藤摸瓜了。
4)“waiting for monitor entry”说明此线程通过 synchronized(obj) {……} 申请进入了临界区,从而进入了下图1中的“Entry Set”队列,但该 obj 对应的 monitor 被其他线程拥有,所以本线程在 Entry Set 队列中等待。
5)第一行里,"RMI TCP Connection(267865)-172.16.5.25"是 Thread Name 。tid指Java Thread id。nid指native线程的id。prio是线程优先级。[0x00007fd4f8684000]是线程栈起始地址。
Dump文件中的线程状态含义及注意事项

含义如下所示:

1、Deadlock:死锁线程,一般指多个线程调用间,进入相互资源占用,导致一直等待无法释放的情况。

2、Runnable:一般指该线程正在执行状态中,该线程占用了资源,正在处理某个请求,有可能正在传递SQL到数据库执行,有可能在对某个文件操作,有可能进行数据类型等转换。

3、Waiting on condition:等待资源,或等待某个条件的发生。具体原因需结合  stacktrace来分析。

 (1)如果堆栈信息明确是应用代码,则证明该线程正在等待资源。一般是大量读取某资源,且该资源采用了资源锁的情况下,线程进入等待状态,等待资源的读取。

 (2)又或者,正在等待其他线程的执行等。

 (3)如果发现有大量的线程都在处在 Wait on condition,从线程 stack看,正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。

  • 一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写;
  • 另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。

 (4)另外一种出现 Wait on condition的常见情况是该线程在 sleep,等待 sleep的时间到了时候,将被唤醒。

4、Blocked:线程阻塞,是指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。

5、Waiting for monitor entry 和 in Object.wait():Monitor是  Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。从下图1中可以看出,每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是  “in Object.wait()”

堆栈信息分析第3张

 

综合示范二:Waiting on condition 和 TIMED_WAITING
实例如下:
"RMI TCP Connection(idle)" daemon prio=10 tid=0x00007fd50834e800 nid=0x56b2 waiting on condition [0x00007fd4f1a59000]
   java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
parking to wait for  <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:424)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:323)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:874)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:945)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:662)
1)“TIMED_WAITING (parking)”中的 timed_waiting 指等待状态,但这里指定了时间,到达指定的时间后自动退出等待状态;parking指线程处于挂起中。
2)“waiting on condition”需要与堆栈中的“parking to wait for  <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack)”结合来看。首先,本线程肯定是在等待某个条件的发生,来把自己唤醒。其次,SynchronousQueue 并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到 SynchronousQueue 中时必须有另一个线程正在等待接受移交的任务,因此这就是本线程在等待的条件。
3)别的就看不出来了。

综合示范三:in Obejct.wait() 和 TIMED_WAITING

实例如下:
"RMI RenewClean-[172.16.5.19:28475]" daemon prio=10 tid=0x0000000041428800 nid=0xb09 in Object.wait() [0x00007f34f4bd0000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
waiting on <0x00000000aa672478> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
locked <0x00000000aa672478> (a java.lang.ref.ReferenceQueue$Lock)
at sun.rmi.transport.DGCClient$EndpointEntry$RenewCleanThread.run(DGCClient.java:516)
at java.lang.Thread.run(Thread.java:662)
1)“TIMED_WAITING (on object monitor)”,对于本例而言,是因为本线程调用了 java.lang.Object.wait(long timeout) 而进入等待状态。
2)“Wait Set”中等待的线程状态就是“ in Object.wait() ”。当线程获得了 Monitor,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了 Monitor,进入 “Wait Set”队列。只有当别的线程在该对象上调用了  notify() 或者 notifyAll() ,“ Wait Set”队列中线程才得到机会去竞争,但是只有一个线程获得对象的 Monitor,恢复到运行态。 
3)RMI RenewClean 是 DGCClient 的一部分。DGC 指的是 Distributed GC,即分布式垃圾回收。
4)请注意,是先 locked <0x00000000aa672478>,后 waiting on <0x00000000aa672478>,之所以先锁再等同一个对象,请看下面它的代码实现:
static private class  Lock { };
private Lock lock = new Lock();
public Reference<? extends T> remove(long timeout)
{
    synchronized (lock) {
    Reference<? extends T> r = reallyPoll();
        if (r != null) return r;
        for (;;) {
            lock.wait(timeout);
            r = reallyPoll();
            ……
       }
}
即,线程的执行中,先用 synchronized 获得了这个对象的 Monitor(对应于  locked <0x00000000aa672478> );当执行到 lock.wait(timeout);,线程就放弃了 Monitor 的所有权,进入“Wait Set”队列(对应于  waiting on <0x00000000aa672478> )。
5)从堆栈信息看,是正在清理 remote references to remote objects ,引用的租约到了,分布式垃圾回收在逐一清理呢。

参见:http://blog.csdn.net/kobejayandy/article/details/36030547

jstat 用来统计 gc 的相关信息:各代空间大小、比例、gc 时间等。示例:jstat -gc <pid> <interval> <count> 输出:

堆栈信息分析第4张

关于jstat详情请参见:http://blog.csdn.net/chaofanwei/article/details/11758395

免责声明:文章转载自《堆栈信息分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇npm 进阶命令知多少(一)拜耳阵列(Bayer Pattern)简介下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Invoke()方法的使用

在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。 正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等...

Java高并发网络编程(四)Netty

在网络应用开发的过程中,直接使用JDK提供的NIO的API,比较繁琐,而且想要进行性能提升,还需要结合多线程技术。 由于网络编程本身的复杂性,以及JDK API开发的使用难度较高,所以在开源社区中,涌现出来了很多对JDK NIO进行封装、增强的网络编程框架,比如Netty、Mina等。 一、Netty简介 https://netty.io/官网 Netty...

高性能缓存架构

极客时间:《从 0 开始学架构》:高性能缓存架构 1、引言 前几章节分别从读写分离、分库分表以及数据库的选择等方面来提升系统的性能,但在某些复杂的业务场景下,单纯的提高存储系统的性能是不够的,典型的场景如下: 需要经过复杂运算后得出的数据,存储系统无能为力 读多写少的数据,存储系统有心无力。如写一次,读多次 缓存就是为了弥补存储系统在这些复杂业务场景下...

Windows中的线程命名杂谈

Windows允许您为进程中的线程指定名称,然后调试器可以显示这些名称。这是一个很好的解决方案,但这是一个很好的解决方案。Windows 10 Creators更新(SetThreadDescription)中添加了一个新的线程命名API。Chrome现在使用SetThreadDescription来命名它的线程(当这个函数可用时)。chromiumrep...

【Win 10应用开发】多窗口视图

Windows App一般情况下,同一时刻只能有一个应用程序实例在运行,为了在特殊需求下可以同时呈现不同的UI,SDK提供了多视图操作支持。 应用程序可以创建新的应用视图,以新的视图为基础可以呈现与主视图不同的内容,但又不影响主视图的UI。这些视图既可以在同一个窗口中切换,也可以用新的窗口来呈现新的视图。这些窗口,用户可以拖放到不同的虚拟桌面中。 其实,视...

Angular2中实现基于TypeScript的对象合并方法:extend()

TypeScript里面没有现成的合并对象的方法,这里借鉴jQuery里的$.extend()方法。写了一个TypeScript的对象合并方法,使用方法和jQuery一样。 部分代码和jQuery代码略有不同,主要是判断元素是否为数组和纯对象的部分。jQuery中有方法可直接判断元素是否为数组($.isArray())和对象($.isPlainObject...