我试图更多地了解java,特别是关于内存管理和线程。 出于这个原因,我最近有兴趣查看线程转储。
以下是使用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)
首先,我对一些变量名称有疑问:
然后是堆栈跟踪本身:
我认为锁定这个词在某种程度上与等待条件有关,但是,我错了。事实上,我想知道为什么锁定重复三次,但线程处于可运行状态,如同一个转储中所示:
"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?
为什么堆栈跟踪如此短暂而没有任何线程类的证据?
如果你能回答我的所有问题,我将非常感激。
由于
答案 0 :(得分:107)
TID是thead id,NID是:本机线程ID。此ID高度依赖于平台。它是jstack线程转储中的NID。在Windows上,它只是进程中的操作系统级线程ID。在Linux和Solaris上,它是线程的PID(反过来又是一个轻量级的过程)。在Mac OS X上,它被称为本机pthread_t值。
转到此链接:Java-level thread ID:有关这两个术语的定义和进一步说明。
在IBM的网站上,我找到了这个链接:How to interpret a thread dump。更详细地介绍了这一点:
它解释了等待的意思: 锁可防止多个实体访问共享资源。 Java™中的每个对象都有一个关联的锁(通过使用同步块或方法获得)。对于JVM,线程在JVM中竞争各种资源并锁定Java对象。
然后它将监视器描述为一种特殊的锁定机制,在JVM中使用它以允许线程之间的灵活同步。出于本节的目的,请阅读术语监视器并可互换锁定。
然后它走得更远:
为了避免在每个对象上都有监视器,JVM通常使用类或方法块中的标志来指示该项目已被锁定。大多数情况下,一段代码会在没有争用的情况下传输一些锁定的部分。因此,监护人标志足以保护这段代码。这称为平板显示器。但是,如果另一个线程想要访问某些已锁定的代码,则会发生真正的争用。 JVM现在必须创建(或膨胀)监视器对象以保存第二个线程,并安排信号机制来协调对代码段的访问。此监视器现在称为充气监视器。
这是对您在线程转储中看到的内容的更深入的解释。 Java线程由操作系统的本机线程实现。每个线程都用粗体表示,例如:
“Thread-1”(TID:0x9017A0,sys_thread_t:0x23EAC8,状态:R,本机ID:0x6E4)prio = 5
*以下6个项目解释了这一点,因为我从示例中匹配了它们,括号[]中的值:
“wait on”似乎是与jvm本身相关联的守护程序线程,而不是应用程序线程perse。当你得到一个“in Object.wait()”时,这意味着守护程序线程,这里的“终结器”正在等待关于对象锁定的通知,在这种情况下,它会显示它正在等待的通知: “ - 等待&lt; 0x27ef0288&gt;(java.lang.ref.ReferenceQueue $ Lock)”
ReferenceQueue的定义是: 参考队列,在检测到适当的可达性更改后,垃圾收集器将附加的注册引用对象附加到该引用队列。
终结器线程运行,因此垃圾收集操作以清理与对象关联的资源。如果我看到它的核心,终结器无法获得对此对象的锁定:java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)因为java对象正在运行一个方法,所以终结器线程是锁定,直到该对象完成它的当前任务。
此外,终结器不只是寻求回收内存,它比清理资源更具参与性。我需要对它进行更多的研究,但如果你有文件打开,套接字等...与对象方法相关,那么终结器也可以解决这些问题。
在Object.wait之后的平方括号中的数字是多少 线程转储?
它是线程内存中的指针。以下是更详细的说明:
C.4.1线程信息
线程部分的第一部分显示引发致命错误的线程,如下所示:
Current thread (0x0805ac88): JavaThread "main" [_thread_in_native, id=21139]
| | | | +-- ID
| | | +------------- state
| | +-------------------------- name
| +------------------------------------ type
+-------------------------------------------------- pointer
线程指针是指向Java VM内部线程结构的指针。除非您正在调试实时Java VM或核心文件,否则通常没有意义。
最后的描述来自:Troubleshooting Guide for Java SE 6 with HotSpot VM
以下是有关线程转储的更多链接:
答案 1 :(得分:9)
继@James Drinkard的优秀答案:
请注意,根据底层实现,本机方法中阻止的线程的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.
事实证明,此描述还包括在诸如轮询或读取操作之类的OS调用中被阻止 - 可能是因为无法保证JVM可以知道本机方法调用何时在操作系统级别被阻止。
我见过的许多关于JVM线程转储的讨论要么完全忽略了这种可能性,要么在没有考虑其影响的情况下轻率地略过它 - 尤其是监控工具可能会混淆地报告几个这样的线程正在“运行”,而且他们都在100%运行。