获取Java线程的ID和堆栈跟踪以解决运行超时的Java线程问题

18

在我最繁忙的生产环境中,有时会出现一个单线程似乎陷入了无限循环。经过大量研究和调试后,我还没有弄清是谁干的,但似乎应该是有可能找到问题根源的。以下是详细信息:

目前的调试说明:

1)ps -eL 18975 告诉我这个问题线程的Linux进程ID是19269。

$ps -eL | grep 18975
...
PID   LWP   TTY          TIME CMD
18975 18994 ?        00:00:05 java
18975 19268 ?        00:00:00 java
18975 19269 ?        05:16:49 java
18975 19271 ?        00:01:22 java
18975 19273 ?        00:00:00 java
...

2) jstack -l 18975表示没有死锁,jstack -m 18975无法工作。

3) jstack -l 18975确实为我所有的线程(~400)提供了堆栈跟踪。例如线程堆栈(并不是问题所在):

"http-342.877.573.944-8080-360" daemon prio=10 tid=0x0000002adaba9c00 nid=0x754c in Object.wait() [0x00000000595bc000..0x00000000595bccb0]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on  (a org.apache.tomcat.util.net.JIoEndpoint$Worker)
        at java.lang.Object.wait(Object.java:485)
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.await(JIoEndpoint.java:416)
        - locked  (a org.apache.tomcat.util.net.JIoEndpoint$Worker)
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:442)
        at java.lang.Thread.run(Thread.java:619)

4) ps -eL输出的线程ID与jstack的输出不匹配,或者说至少我看不到它。(jstack文档有点稀疏。)

5) 没有重型IO、内存使用或其他相应的活动线索可用。

平台:

  • Java 6
  • Tomcat 6
  • RHEL 4 (64位)

有人知道我如何从Linux ps输出中找出我的问题Java线程吗?离成功只差一步...

5个回答

13

看起来 jstack 输出中的 nid 是 Linux LWP id。

"http-342.877.573.944-8080-360" daemon prio=10 tid=0x0000002adaba9c00 nid=0x754c in Object.wait() [0x00000000595bc000..0x00000000595bccb0]

将nid转换为十进制,即可得到LWP id。在您的情况下,0x754c相当于30028。这个过程在我们的ps输出中没有显示出来,但它可能是您省略以节省空间的LWP之一。

这里有一个小的Perl片段,可以用来管道jstack的输出:

#!/usr/bin/perl -w
while (<>) {
    if (/nid=(0x[[:xdigit:]]+)/) {
        $lwp = hex($1);
        s/nid=/lwp=$lwp nid=/;
    }
    print;
}

虽然我一年前尝试过这个方法,但是现在它对我起作用了。我使用的是新版本的Java,也许这就是原因。或者我只是犯了一个hexNid2dec(pid)的数学错误。或者可能是我做了其他什么极其愚蠢的事情。 - Stu Thompson
仅供参考,“nid”是“本地ID”的缩写,它是Java线程底层系统的本地标识符。 - Cowan
@Cowan,本地ID是什么?PID还是LWPID?根据这篇文章:http://blogs.manageengine.com/appmanager/2011/02/09/identify-java-code-consuming-high-cpu-in-linux-linking-jvm-thread-and-linux-pid,应该是PID,但是这个答案表明它是LWPID。 - Sawyer
作为一行代码:jstack <PID> | perl -ne 'if (/nid=(0x[[:xdigit:]]+)/) { $lwp = hex($1); s/nid=/lwp=$lwp nid=/; } print;' - tekumara

7
您可以使用 JConsole 查看线程的堆栈跟踪。
如果您使用的是 JDK 1.6.0_07 或更高版本,则还可以使用 visualvm
这两个工具都提供了应用程序中所有运行线程的良好视图。VisualVM 更加出色,但希望查看所有线程能够帮助您跟踪运行超时的线程。
检查始终处于 RUNNING 状态的线程。当我们遇到运行超时的线程时,堆栈跟踪会不断变化。因此,我们能够确定循环调用的方法,并跟踪循环。

但是仅仅通过jconsole我无法确定哪个线程出了问题。:( 有超过400个线程,而且ps输出也没有帮助,所以我应该能够进行权威分析。 - Stu Thompson
+1 for jconsole。jstack听起来似乎不受支持:“注意-此实用程序不受支持,可能在将来的J2SE SDK版本中可用或不可用。jstack目前不适用于Windows平台或Linux Itanium平台。” - matt b
嗯...好的,有点笨拙,最好在凌晨2点完成,但我理解你的逻辑...那可能行得通... - Stu Thompson
1
仅仅因为 jstack 不再受支持并不意味着它不是一个有效的调试工具。 - Stu Thompson
1
VisualVM有一个名为“仅显示活动线程”的过滤器,可能对您有所帮助。 - matt b

1

好的,有用的答案!

对于Linux,使用ps -efL命令,-L选项将显示LWPs。 顺便提一下,
"http-342.877.573.944-8080-360" daemon prio=10 表示 "线程名(由JVM给出)" 运行模式(从pid继承) 优先级(从pid继承)


0

关于SUN

请注意,默认情况下prstat显示的是轻量级进程的数量,而不是LWPID。

要查看特定用户的所有轻量级进程的信息,请使用-L选项。

prstat -L -v -u weblogic

现在使用LWPID,将其转换为十六进制,并与线程转储中的NID匹配。

0

从记忆中,如果你在控制台上按下CTRL-BREAK,你将得到当前线程的转储和它们的一些堆栈跟踪帧。

从记忆中(我不确定这是IntelliJ IDEA的功能还是Java的默认功能),但它会告诉你哪个线程死锁了,以及它们正在等待的对象。你应该能够将输出重定向到文件,并只搜索DEADLOCKED文本。

JConsole、VisualVM或其他分析工具如JProfiler也会显示线程及其堆栈,但如果你不想使用任何外部工具,我认为CTRL-BREAK会给你想要的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接