如何分析一个Java线程转储?

我想了解更多关于Java的知识,特别是内存pipe理和线程。 出于这个原因,我最近有兴趣看看线程转储。

以下是使用VisualVM(一种用于java的内置工具)的Web应用程序中的几行代码:

"Finalizer" daemon prio=8 tid=0x02b3d000 nid=0x898 in Object.wait() [0x02d0f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x27ef0288> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) - locked <0x27ef0288> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159) Locked ownable synchronizers: - None "Reference Handler" daemon prio=10 tid=0x02b3b800 nid=0x494 in Object.wait() [0x02cbf000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x27ef0310> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:485) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116) - locked <0x27ef0310> (a java.lang.ref.Reference$Lock) 

首先,我对一些variables名称有疑问:

  • tid和nid是什么意思?
  • Object.wait之后的圆括号中的数字是什么?

那么对于堆栈跟踪本身:

  • 这是什么意思等待<…..>(一个java.lang ….)<..>中的数字是什么
  • 这是什么意思locking<…..>(一个java.lang ….)同样的问题,什么在<..>

我认为locking的这个词在某种程度上与等待条件有关,但是我错了。 实际上,我想知道为什么locking重复三次,但线程处于可运行状态,如同在同一个转储中看到的:

 "Thread-0" prio=6 tid=0x02ee3800 nid=0xc1c runnable [0x03eaf000] java.lang.Thread.State: RUNNABLE at java.io.FileInputStream.readBytes(Native Method) at java.io.FileInputStream.read(FileInputStream.java:199) at java.io.BufferedInputStream.read1(BufferedInputStream.java:256) at java.io.BufferedInputStream.read(BufferedInputStream.java:317) - locked <0x23963378> (a java.io.BufferedInputStream) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158) - locked <0x23968450> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:167) at java.io.BufferedReader.fill(BufferedReader.java:136) at java.io.BufferedReader.readLine(BufferedReader.java:299) - locked <0x23968450> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:362) at org.codehaus.plexus.util.cli.StreamPumper.run(StreamPumper.java:145) 

最后,这是最糟糕的:

 "CompilerThread0" daemon prio=10 tid=0x02b81000 nid=0x698 waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE 

此线程处于可运行状态,但正在等待状态。 什么条件和什么是0x00000?

为什么堆栈跟踪很短,没有任何线程类的证据?

如果你能回答我所有的问题,我将非常感激。

谢谢

TID是addID,NID是:本地线程ID。 这个ID是高度依赖于平台的。 这是jstack线程转储中的NID。 在Windows上,它只是一个进程中的操作系统级别的线程ID。 在Linux和Solaris上,它是线程的PID(这又是一个轻量级进程)。 在Mac OS X上,它被称为本地pthread_t值。

转到此链接: Java级线程标识 :定义和这两个术语的进一步解释。

在IBM的网站上,我发现这个链接: 如何解释一个线程转储 。 这更详细地介绍了这一点:

它解释了等待意味着什么:锁可以防止多个实体访问共享资源。 Java™中的每个对象都有一个关联的锁(通过使用同步块或方法获得)。 在JVM的情况下,线程竞争JVM中的各种资源并lockingJava对象。

然后将监视器描述为JVM中使用的一种特殊的locking机制,以允许线程间的灵活同步。 为了达到本节的目的,请阅读术语“监视器”并互换locking。

然后进一步:

为了避免在每个对象上都有监视器,JVM通常在类或方法块中使用一个标志来指示该项目被locking。 大多数情况下,一段代码将转换某个locking的部分而不会引起争用。 因此,监护人标志足以保护这段代码。 这被称为平板显示器。 但是,如果另一个线程想要访问一些被locking的代码,就会发生真正的争用。 JVM现在必须创build(或膨胀)监视对象来保存第二个线程,并安排信号机制来协调对代码段的访问。 这台显示器现在被称为充气显示器。

这是一个更深入的解释,你从线程转储线上看到什么。 Java线程由操作系统的本地线程实现。 每个线程都以粗体显示,例如:

“线程1”(TID:0x9017A0,sys_thread_t:0x23EAC8,状态:R,本机ID:0x6E4)prio = 5

*以下6个项目解释了这一点,因为我已经从示例中匹配了它们,括号[]中的值:

  1. 名称[ TID:0x9017A0 ],
  2. 标识符[ sys_thread_t ],
  3. JVM数据结构地址[ 0x23EAC8 ],
  4. 当前状态[ state:R ],
  5. 本地线程标识符[ 0x6E4 ],
  6. 和优先级[ prio = 5 ]。

“等待”似乎是一个与jvm本身相关的守护线程,而不是应用程序线程。 当你得到一个“在Object.wait()”,这意味着守护进程线程,“终结器”在这里,正在等待一个对象上的锁的通知,在这种情况下,它显示你等待什么通知:“ – 等待<0x27ef0288>(一个java.lang.ref.ReferenceQueue $ Lock)“

ReferenceQueue的定义是:引用队列,在检测到适当的可访问性更改后,垃圾回收器将附加已注册的引用对象。

终结器线程运行,垃圾收集操作清理与对象相关的资源。 如果我正确地看到它,终结器不能获得对这个对象的锁:java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118),因为java对象正在运行一个方法,所以终结器线程是locking,直到该对象完成它的当前任务。

而且,终结者不仅仅是寻找回收内存,它比清理资源更为复杂。 我需要对它进行更多的研究,但是如果你有文件打开,套接字等等与对象方法有关,那么终结器也将把这些项目解放出来。

线程转储中的Object.wait之后的圆括号中的数字是什么?

它是线程内存中的一个指针。 这里是更详细的描述:

C.4.1线程信息

线程部分的第一部分显示了引发致命错误的线程,如下所示:

 Current thread (0x0805ac88): JavaThread "main" [_thread_in_native, id=21139] | | | | +-- ID | | | +------------- state | | +-------------------------- name | +------------------------------------ type +-------------------------------------------------- pointer 

线程指针是指向Java VM内部线程结构的指针。 除非您正在debugging活动的Java虚拟机或核心文件,否则通常不感兴趣。

最后的描述来自: 使用HotSpot VM的Java SE 6的故障排除指南

这里有几个线程转储的链接:

  • 线程如何工作
  • 如何分析线程转储
  • Java线程转储
  • Java VM线程
  • Stackoverflow问题: 如何映射线程

除了@詹姆斯·沃特纳德的杰出答案:

请注意,根据底层实现,在本机方法中阻塞的线程的java.lang.Thread.State可能会报告为RUNNABLE ,其中A thread in the runnable state is executing in the Java virtual machine but it may be waiting for other resources from the operating system such as processor.

事实certificate,这个描述还包含了在诸如轮询或读操作之类的OS调用中被阻塞 – 大概是因为不能保证JVM能够知道OS本地方法调用何时被阻塞。

我所见过的JVM线程转储的许多讨论都完全忽略了这种可能性,或者在不考虑其含义的情况下轻率地忽略这种可能性 – 其中最重要的是监控工具可能会混淆地报告几个这样的线程正在运行,他们都在100%运行。

尝试http://fastthread.io

对于很多情况来说,这样做已经足够了。