http://www.oschina.net/translate/java-thread-dump

java线程转储

java的线程转储可以被定义为JVM中在某一个给定的时刻运行的所有线程的快照。一个线程转储可能包含一个单独的线程或者多个线程。在多线程环境中,比如J2EE应用服务器,将会有许多线程和线程组。每一个线程都有它自己的调用堆栈,在一个给定时刻,表现为一个独立功能。线程转储将会提供JVM中所有线程的堆栈信息,对于特定的线程也会给出更多信息。

java虚拟机进程和java线程

java虚拟机,或者称为JVM,是一个操作系统级别的进程。java线程是JVM进程的子进程或者轻量级进程(Solar中的叫法)。

生成java线程转储

线程转储可以通过向JVM进程发送一个SIGQUIT信号来生成。有两种不同方式来向进程发送这个信号:

在Unix中,使用“kill -3<pid>”命令,pid表示JVM进程的ID。

在Windows中,在JVM运行时按下CTRL+BREAK键。

[fox]:jstack jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息。

java线程状态

每一个java线程总是处于其生命周期的四个状态之一。

Runnable-线程正在运行,或者准备好获取CPU时间后运行。JRockit线程转储中把这种状态当做Active。

Waiting on Monitor-线程休眠,或者在等待一个对象,或者等待被其他线程唤醒。在线程对象中调用sleep()方法,或者在一个对象中调用wait()方法时就会有这种情况发生。

举个例子,在WebLogc服务器中,空闲的执行线程处于这种状态,他们会一直等待直到一个Socket reader线程有新的任务才唤醒他们。堆栈信息就会如下所示:

"ExecuteThread: '2' for queue: 'weblogic.admin.RMI'" daemon prio=5 tid=0x1752F040 nid=0x180c in Object.wait() [1887f000..1887fd8c]
at java.lang.Object.wait(Native Method) waiting on <04134D98> (a weblogic.kernel.ExecuteThread)
at java.lang.Object.wait(Object.java:426)
at weblogic.kernel.ExecuteThread.waitForRequest(ExecuteThread.java:126)
locked <04134D98> (a weblogic.kernel.ExecuteThread)
at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:145)

在某些别的版本的JVM中称这种状态是CW,Object.wait()(像上面那样)。JRockit称之为WAITING。

Waiting for Monitor Entry——线程在等待获取一个对象的锁(其他线程可能持有这个同步锁)。这种情况发生在当两个或者更多线程尝试执行一段同步代码时。注意“锁”总是针对于一个对象而不是针对一个单独的方法。

这种情况的线程的简单堆栈信息如下:

"ExecuteThread: '24' for queue: 'DisplayExecuteQueue'" daemon prio=5 tid=0x5541b0 nid=0x3b waiting for monitor entry [49b7f000..49b7fc24]

at weblogic.cluster.replication.ReplicationManager.createSecondary (ReplicationManager.java:908)
- waiting to lock <6c4b9130> (a java.lang.Object)
at weblogic.cluster.replication.ReplicationManager.updateSecondary (ReplicationManager.java:715)
at weblogic.servlet.internal.session.ReplicatedSessionData.syncSession (ReplicatedSessonData.java:459)
- locked <6c408700> (a weblogic.servlet.internal.session.ReplicatedSessionData)
at weblogic.servlet.internal.session.ReplicatedSessionContext.sync (ReplicatedSessionContext.java:134)
- locked <6c408700> (aweblogic.servlet.internal.session.ReplicatedSessionData)
at weblogic.servlet.internal.ServletRequestImpl.syncSession (ServletRequestImpl.java:2418)
at weblogic.servlet.internal.WebAppServletContext.invokeServlet (WebAppServletContext.java:3137)
at weblogic.servlet.internal.ServletRequestImpl.execute (ServletRequestImpl.java:2544)
at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:153)
at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:134)

在以上堆栈信息中,你可以看到这个线程持有一个对象锁 (6c408700) ,并在等待另一个对象锁(6c4b9130)。

某些别的版本的JVM可能不会在堆栈信息中给出对象的ID和锁的信息。同样的状态,在某些JVM版本中可能被称为“MW”。JRockit称之为LOCKED。

分析一个Java线程

为了可以理解/分析线程转储,首先要理解线程转储的各个部分。让我们先拿一个简单的线程堆栈为例,并且去了解他的每个部分。

"ExecuteThread: '1' " daemon prio=5 tid=0x628330 nid=0xf runnable [0xe4881000..0xe48819e0]
at com.vantive.vanjavi.VanJavi.VanCreateForm(Native Method)
at com.vantive.vanjavi.VanMain.open(VanMain.java:53)
at jsp_servlet._so.__newServiceOrder.printSOSection( __newServiceOrder.java:3547)
at jsp_servlet._so.__newServiceOrder._jspService (__newServiceOrder.java:5652)
at weblogic.servlet.jsp.JspBase.service(JspBase.java:27)
at weblogic.servlet.internal.ServletStubImpl.invokeServlet (ServletStubImpl.java:265)
at weblogic.servlet.internal.ServletStubImpl.invokeServlet (ServletStubImpl.java:200)
at weblogic.servlet.internal.WebAppServletContext.invokeServlet (WebAppServletContext.java:2495)
at weblogic.servlet.internal.ServletRequestImpl.execute (ServletRequestImpl.java:2204)
at weblogic.kernel.ExecuteThread.execute (ExecuteThread.java:139)
at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:120)

In the above Thread Dump, the interesting part to is the first line. The rest of the stuff is nothing more than a general stack trace. Lets analyze the first line here

Execute Thread : 1 说明了线程的名字

daemon 表明这个线程是一个守护线程

prio=5 线程的优先级 (默认是5)

tid Java的线程Id (这个线程在当前虚拟机中的唯一标识).

nid 线程本地标识. 也就是Solaris中的LWP,线程在操作系统中的标识

runnable 线程的状态 (参考上面的)

[x..y] 当前运行的线程在堆中的地址范围

这个线程转储的剩余部分是调用堆栈。在这个例子中,这个线程(Execute Thread 1)是操作系统守护线程,当前正在执行一个本地方法vanCreateForm()。

使用线程转储

在这部分,我将描述几个用例来说明线程转储是非常有用的。

高CPU占用率

诊断

应用程序看起来几乎让CPU的占用率达到了100%,但是系统吞吐量却明显下降。开始于高负载的CPU性能很差。

线程转储

通过所有的线程转储,可以看到一个或多个线程在同一个操作中罢工了。

解决办法

  • 为一个特定的调用流程(比如说网页上的form提交),在流程完成之前,生成一系列的线程转储(大约5~7个)
  • 查找线程转储中的“runnable”线程。如果每一个线程看起来运行良好(每一个线程调用的方法都不相同),这些线程就是正在处理事务中,而且有可能并不是这次事件的罪魁祸首。如果通过所有的线程转储,发现线程正在执行同一个方法(同样的行号),几乎就可以确定这就是罪魁祸首了。那就可以查看代码,来做代码级别的分析了。你肯定也能从代码中找到解决问题的灵感。

低CPU负载率和很长的响应时间

诊断

这通常在一个高I/O限制的系统处于高负载的时候发生。CPU的占用率很低,只有几个线程在消耗CPU的时间片。然而应用的响应时间却很长。

线程转储

一部分或者全部运行线程看起来就像是在一个I/O操作中罢工了,比如文件读/写或者数据库的操作。

解决方法

了解你系统中的I/O操作。使用缓存以减少应用与数据库之间的交互。

应用/服务宕机

诊断

一个应用或者一个运行这个应用的服务JVM宕机(变得停止响应)

线程转储

  • 在获得的所有线程转储中,可以看到所有的运行线程都在同一个操作中罢工了。服务器没有可用的线程,因为没有一个线程能够完成他自己的操作。
  • 或许有很多线程在等待一个锁。当一个运行的线程持有一个对象锁不释放,而其他的线程恰好在等待这个对象锁的时候就会发生。

解决方法

  • 检查死锁,通常简单情况下(线程A在等待线程B,同时B也在等待A),JVM通常会检测到死锁。但是,你需要了解在这个时刻锁的状态,以确认这时候是否涉及到一个复杂的死锁了。
  • 复查同步方法/代码块,尽可能的将不需要同步的代码移出同步区,以减少同步区的大小。
  • 这种问题还有一个可能,就是访问一个远程的资源/组件的响应超时设置的太长。在访问远程对象时设置一个合理的超时时间,这样就能够在远程系统失去响应时抛出一个可以捕获的异常。
  • 如果所有的线程在等待一个资源(比如EJB/DB连接),考虑增加这些资源的对象池大小。

补充:

http://www.blogjava.net/jzone/articles/303979.html

1.线程的状态

线程的状态分析

正如我们刚看到的那样,线程的状态是一个重要的指标,它会显示在线程 Stacktrace的头一行结尾的地方。那么线程常见的有哪些状态呢?线程在什么样的情况下会进入这种状态呢?我们能从中发现什么线索?

1.1 Runnable 
该状态表示线程具备所有运行条件,在运行队列中准备操作系统的调度,或者正在运行。

1.2 Wait on condition 
该状态出现在线程等待某个条件的发生。具体是什么原因,可以结合 stacktrace来分析。最常见的情况是线程在等待网络的读写,比如当网络数据没有准备好读时,线程处于这种等待状态,而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。在 Java引入 NewIO之前,对于每个网络连接,都有一个对应的线程来处理网络的读写操作,即使没有可读写的数据,线程仍然阻塞在读写操作上,这样有可能造成资源浪费,而且给操作系统的线程调度也带来压力。在 NewIO里采用了新的机制,编写的服务器程序的性能和可扩展性都得到提高。

如果发现有大量的线程都在处在 Wait on condition,从线程 stack看, 正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几 乎消耗了所有的带宽,仍然有大量数据等待网络读 写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。所以要结合系统的一些性能观察工具来综合分析,比如 netstat统计单位时间的发送包的数目,如果很明显超过了所在网络带宽的限制 ; 观察 cpu的利用率,如果系统态的 CPU时间,相对于用户态的 CPU时间比例较高;如果程序运行在 Solaris 10平台上,可以用 dtrace工具看系统调用的情况,如果观察到 read/write的系统调用的次数或者运行时间遥遥领先;这些都指向由于网络带宽所限导致的网络瓶颈。

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

1.3 Waiting for monitor entry 和 in Object.wait() 
在多线程的 JAVA程序中,实现线程之间的同步,就要说说 Monitor。 Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。下 面这个图,描述了线程和 Monitor之间关系,以 及线程的状态转换图: 

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

先看 “Entry Set”里面的线程。我们称被 synchronized保护起来的代码段为临界区。当一个线程申请进入临界区时,它就进入了 “Entry Set”队列。对应的 code就像:

synchronized(obj) { 
......... 
}

这时有两种可能性: 
1)该 monitor不被其它线程拥有, Entry Set里面也没有其它等待线程。本线程即成为相应类或者对象的 Monitor的 Owner,执行临界区的代码 
2)该 monitor被其它线程拥有,本线程在 Entry Set队列中等待。

在第一种情况下,线程将处于 “Runnable”的状态,而第二种情况下,线程 DUMP会显示处于 “waiting for monitor entry”。如下所示:

"Thread-0" prio=10 tid=0x08222eb0 nid=0x9 waiting for monitor entry [0xf927b000..0xf927bdb8] 
at testthread.WaitThread.run(WaitThread.java:39) 
- waiting to lock <0xef63bf08> (a java.lang.Object) 
- locked <0xef63beb8> (a java.util.ArrayList) 
at java.lang.Thread.run(Thread.java:595)

临界区的设置,是为了保证其内部的代码执行的原子性和完整性。但是因为临界区在任何时间只允许线程串行通过,这 和我们多线程的程序的初衷是相反的。 如果在多线程的程序中,大量使用 synchronized,或者不适当的使用了它,会造成大量线程在临界区的入口等待,造成系统的性能大幅下降。如果在线程 DUMP中发现了这个情况,应该审查源码,改进程序。

现在我们再来看现在线程为什么会进入 “Wait Set”。当线程获得了 Monitor,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了 Monitor,进入 “Wait Set”队列。只有当别的线程在该对象上调用了 notify() 或者 notifyAll() , “ Wait Set”队列中线程才得到机会去竞争,但是只有一个线程获得对象的 Monitor,恢复到运行态。在 “Wait Set”中的线程, DUMP中表现为: in Object.wait(),类似于:

"Thread-1" prio=10 tid=0x08223250 nid=0xa in Object.wait() [0xef47a000..0xef47aa38] 
at java.lang.Object.wait(Native Method) 
- waiting on <0xef63beb8> (a java.util.ArrayList) 
at java.lang.Object.wait(Object.java:474) 
at testthread.MyWaitThread.run(MyWaitThread.java:40) 
- locked <0xef63beb8> (a java.util.ArrayList) 
at java.lang.Thread.run(Thread.java:595)

仔细观察上面的 DUMP信息,你会发现它有以下两行:

- locked <0xef63beb8> (a java.util.ArrayList) 
- waiting on <0xef63beb8> (a java.util.ArrayList)

这里需要解释一下,为什么先 lock了这个对象,然后又 waiting on同一个对象呢?让我们看看这个线程对应的代码:

synchronized(obj) { 
               ......... 
               obj.wait(); 
               ......... 
        }

线程的执行中,先用 synchronized 获得了这个对象的 Monitor(对应于 locked <0xef63beb8> )。当执行到 obj.wait(), 线程即放弃了 Monitor的所有权,进入 “wait set”队列(对应于 waiting on <0xef63beb8> )。

往往在你的程序中,会出现多个类似的线程,他们都有相似的 DUMP信息。这也可能是正常的。比如,在程序中,有多个服务线程,设计成从一个队列里面读取请求数据。这个队列就是 lock以及 waiting on的对象。当队列为空的时候,这些线程都会在这个队列上等待,直到队列有了数据,这些线程被 Notify,当然只有一个线程获得了 lock,继续执行,而其它线程继续等待。

Java 线程转储 [转]的更多相关文章

  1. Java 线程转储

    软件维护是一个枯燥而又有挑战性的工作.只要软件功能符合预期,那么这个工作就是好的.设想一个这样的情景,你的电话半夜也一直在响(这不是一个令人愉快的感受,是吧?)任何软件系统,无论它当初是被设计的多好, ...

  2. 获取Java线程转储的常用方法

    1. 线程转储简介 线程转储(Thread Dump)就是JVM中所有线程状态信息的一次快照. 线程转储一般使用文本格式, 可以将其保存到文本文件中, 然后人工查看和分析, 或者使用工具/API自动分 ...

  3. Java 死锁诊断 -- 线程转储

    java线程转储 java的线程转储可以被定义为JVM中在某一个给定的时刻运行的所有线程的快照.一个线程转储可能包含一个单独的线程或者多个线程.在多线程环境中,比如J2EE应用服务器,将会有许多线程和 ...

  4. Java并发之线程转储

    一.java线程转储 java的线程转储可以被定义为JVM中在某一个给定的时刻运行的所有线程的快照.一个线程转储可能包含一个单独的线程或者多个线程.在多线程环境中,比如J2EE应用服务器,将会有许多线 ...

  5. Java线程与多线程教程

    本文由 ImportNew - liken 翻译自 Journaldev.   Java线程是执行某些任务的轻量级进程.Java通过Thread类提供多线程支持,应用可以创建并发执行的多个线程. 应用 ...

  6. java线程面试

    1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成 ...

  7. 【Java】线程转储分析 ThreadDump

    [[TOC]] 通过分析 ThreadDump 来查询Java程序运行情况 获取线程转储文件 有多种方式可以获取转储文件,可参考链接HOW TO TAKE THREAD DUMPS? – 8 OPTI ...

  8. java线程数过高原因分析

    作者:鹿丸不会多项式  出处:http://www.cnblogs.com/hechao123   转载请先与我联系. 一.问题描述 前阵子我们因为B机房故障,将所有的流量切到了A机房,在经历了推送+ ...

  9. Java问题定位之Java线程堆栈分析

    采用Java开发的大型应用系统越来越大,越来越复杂,很多系统集成在一起,整个系统看起来像个黑盒子.系统运行遭遇问题(系统停止响应,运行越来越慢,或者性能低下,甚至系统宕掉),如何速度命中问题的根本原因 ...

随机推荐

  1. 格式化json

    格式化输出JSON var obj = {"args0":{"asynTarget":false,"atomicList":[{" ...

  2. Java IntelliJ IDEA 不能显示项目里的文件结构

    方法一: 关闭IDEA, 然后删除项目文件夹下的.idea文件夹 重新用IDEA工具打开项目 方法二: 菜单:File -> Invalidate Caches / Restart

  3. HURST 1116:选美大赛(LIS+路径输出)

    选美大赛 Time Limit: 1000 MS Memory Limit: 65536 K Total Submit: 1099(318 users) Total Accepted: 349(252 ...

  4. gvim最简化设置,去掉工具栏和菜单栏

    编辑vimrc文件(该文件位于gvim安装目录下),在文件末尾添加以下语句即可 set gfn=Courier_New:h14colorscheme torteset guioptions-=mset ...

  5. jquery trigger函数和triggerHandler函数的对照

    一句话的差别就是:trigger will bubbling jQuery events (not default DOM events) and triggerHnadler will not do ...

  6. 算法设计与分析基础 (Anany Levitin 著)

    第1章 绪论 1.1 什么是算法 1.2 算法问题求解基础 1.2.1 理解问题 1.2.2 了解计算设备的性能 1.2.3 在精确解法和近似解法之间做出选择 1.2.4 算法的设计技术 1.2.5 ...

  7. PHP获取站点根目录

    http://rmingwang.com/php-access-to-the-site-root-directory.html php绝对路径与相对路径详解完整版 http://www.phpthin ...

  8. 20165308 2017-2018-2 《Java程序设计》第四周学习总结

    20165308 2017-2018-2 <Java程序设计>第四周学习总结 知识点 第五章 1.子类声明中通常用关键字extend来定义一个子类. 2.子类和父类在同一包中的继承性,继承 ...

  9. InvokeRequired和Invoke(转)

    C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它.此时它将会在内部调用ne ...

  10. Java ThreadPoolTaskExecutor使用

    1. 配置 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http:/ ...