获取 Java 线程 id 和失控 Java 线程的堆栈跟踪
Posted
技术标签:
【中文标题】获取 Java 线程 id 和失控 Java 线程的堆栈跟踪【英文标题】:Getting the Java thread id and stack trace of run-away Java thread 【发布时间】:2010-09-18 08:05:59 【问题描述】:在我最繁忙的生产安装中,有时我会遇到一个似乎卡在无限循环中的线程。经过大量研究和调试,我还没有弄清楚谁是罪魁祸首,但似乎应该是可能的。以下是血淋淋的细节:
当前调试说明:
1) ps -eL 18975 显示问题子线程的 Linux pid,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”守护进程prio=10 tid=0x0000002adaba9c00 nid=0x754c in Object.wait() [0x00000000595bc000..0x00000000595bccb0] java.lang.Thread.State:等待(在对象监视器上) 在 java.lang.Object.wait(本机方法) - 等待(org.apache.tomcat.util.net.JIoEndpoint$Worker) 在 java.lang.Object.wait(Object.java:485) 在 org.apache.tomcat.util.net.JIoEndpoint$Worker.await(JIoEndpoint.java:416) - 锁定(一个 org.apache.tomcat.util.net.JIoEndpoint$Worker) 在 org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:442) 在 java.lang.Thread.run(Thread.java:619)
4) ps -eL 输出的线程 ID 与 jstack 的输出不匹配,或者至少我看不到它。 (jstack 文档有点稀疏。)
5) 没有繁重的 IO、内存使用或其他相应的活动线索可供使用。
平台:
Java 6 雄猫 6 RHEL 4(64 位)有谁知道我如何从 linux ps 输出到我的问题子 java 线程建立连接?这么近,这么远……
【问题讨论】:
【参考方案1】:看起来 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 sn-p,您可以使用管道将 jstack 的输出传送到:
#!/usr/bin/perl -w
while (<>)
if (/nid=(0x[[:xdigit:]]+)/)
$lwp = hex($1);
s/nid=/lwp=$lwp nid=/;
print;
【讨论】:
虽然我在一年前尝试过但没有成功,但它现在对我有用。我正在使用新版本的 Java,所以也许就是这样。或者我刚刚犯了一个hexNid2dec(pid)
数学错误。或者,也许正在做其他令人难以置信的愚蠢的事情。
只是为了记录,'nid' 是 'native id' -- Java 线程的底层系统的本地标识符。
@Cowan 本地 ID 是什么? pid 还是 lwpid?根据这篇文章:blogs.manageengine.com/appmanager/2011/02/09/… 它应该是一个 pid,但是这个答案表明它是一个 lwpid。
单线:jstack <PID> | perl -ne 'if (/nid=(0x[[:xdigit:]]+)/) $lwp = hex($1); s/nid=/lwp=$lwp nid=/; print;'
【参考方案2】:
您可以使用JConsole查看线程的堆栈跟踪。
如果您使用JDK 1.6.0_07或以上版本,也可以使用visualvm。
这两个工具都可以很好地查看应用程序中所有正在运行的线程。 visualvm 更好一些,但希望看到所有线程可以帮助您追踪失控线程。
检查始终处于 RUNNING 状态的线程。当我们有一个失控的线程时,堆栈跟踪会不断变化。所以我们能够分辨出循环调用了哪些方法,并追踪到循环。
【讨论】:
但是我无法从 jconsole 判断出哪个线程是问题所在。 :( 400多线程,ps输出很有帮助,应该可以做个权威分析。 +1 用于 jconsole。 Jstack 听起来像是不受支持:“注意 - 此实用程序不受支持,可能会或可能不会在 J2SE SDK 的未来版本中可用。jstack 目前在 Windows 平台或 Linux Itanium 平台上不可用。” (对删除和重新发布感到抱歉。)嗯......好吧,有点笨拙,最好在凌晨 2 点完成,但我明白你的逻辑......这可以工作...... 不支持 jstack 并不意味着它不是一个有效的调试工具。 visualvm 有一个“仅显示实时线程”过滤器,可能对您有所帮助。【参考方案3】:很好,有用的答案!
对于 Linux,使用 ps -efL,-L 选项将显示 LWP。 附带说明一下,“http-342.877.573.944-8080-360”守护进程 prio=10 表示 “ThreadName(由 JVM 给定)”runningmode(继承自 pid)priority(继承自 pid)
【讨论】:
【参考方案4】:如果您在控制台上按 CTRL-BREAK,您会从内存中获得当前线程及其一些堆栈跟踪帧的转储。
从内存中(我不确定这是 IntelliJ IDEa 功能,还是 java 中的默认功能),但它会告诉您哪个线程死锁,以及它们正在等待哪个对象。您应该能够将输出重定向到文件,并且只需 grep 获取 DEADLOCKED 文本。
JConsole、VisualVM 或其他分析器(例如 JProfiler)也会向您显示线程及其堆栈,但是如果您不想使用任何外部工具,我认为 CTRL-BREAK 会为您提供所需的内容。
【讨论】:
【参考方案5】:在太阳
请注意,prstat
默认显示轻量级进程的数量,而不是 LWPID。
要查看特定用户的所有轻量级进程的信息,请使用-L
选项。
prstat -L -v -u weblogic
现在使用 LWPID 并将其转换为十六进制并与线程转储中的 nid 匹配
【讨论】:
以上是关于获取 Java 线程 id 和失控 Java 线程的堆栈跟踪的主要内容,如果未能解决你的问题,请参考以下文章