我在构build服务器上有一个间歇性的问题,构build中的Java进程无法终止,似乎永远继续运行(使用100%的CPU)永远(我已经看到它在周末运行2天以上通常需要大约10分钟)。 kill -9 pid
似乎是停止进程的唯一方法。
我已经尝试过在进程中调用kill -QUIT pid
,但似乎没有产生任何堆栈跟踪到STDOUT(也许它没有响应信号?)。 没有-F强制选项的jstack似乎无法连接到正在运行的JVM,但是使用强制选项,它会产生下面的输出。
不幸的是,即使有这个堆栈跟踪,我也看不到有任何明显的path可供进一步调查。
据我可以告诉它显示了两个已经运行Object.wait的“BLOCKED”线程(它们的堆栈似乎只包含核心Java代码,没有我们的代码),第三个是没有堆栈输出的“IN_VM”。
我应该采取哪些步骤来收集有关问题原因的更多信息(或者更好,但我该如何解决)?
$ /opt/jdk1.6.0_29/bin/jstack -l -F 5546 附加到进程ID 5546,请稍候... debugging器成功连接。 检测到服务器编译 JVM版本是20.4-b02 死锁检测: 没有发现死锁。 使用Printezis位查找对象大小并跳过... 线程5555 :(状态= BLOCKED) locking拥有同步器: - 没有 线程5554 :(状态= BLOCKED) - java.lang.Object.wait(long)@ bci = 0(解释帧) - java.lang.ref.ReferenceQueue.remove(long)@ bci = 44,line = 118(Interpreted frame) - java.lang.ref.ReferenceQueue.remove()@ bci = 2,line = 134(Interpreted frame) - java.lang.ref.Finalizer $ FinalizerThread.run()@ bci = 3,line = 159(Interpreted frame) locking拥有同步器: - 没有 线程5553 :(状态= BLOCKED) - java.lang.Object.wait(long)@ bci = 0(解释帧) - java.lang.Object.wait()@ bci = 2,line = 485(Interpreted frame) - java.lang.ref.Reference $ ReferenceHandler.run()@ bci = 46,line = 116(Interpreted frame) locking拥有同步器: - 没有 线程5548 :(状态= IN_VM) locking拥有同步器: - 没有
(Java版本1.6.0更新29,在Scientific Linux版本6.0上运行)
更新:
运行strace -f -p 894
产生了一个似乎层出不穷的…
[pid 900] sched_yield() = 0 [pid 900] sched_yield() = 0 ...
然后当Ctrl-Cd
Process 894 detached ... Process 900 detached ... Process 909 detached
jmap -histo 894
不连接,但jmap -F -histo 894
返回…
附加到进程ID 894,请稍候... debugging器成功连接。 检测到服务器编译 JVM版本是20.4-b02 遍历堆。 可能还要等一下... 使用Printezis位查找对象大小并跳过... 使用Printezis位查找对象大小并跳过... 对象直方图: 数字#实例#字节类描述 -------------------------------------------------- ------------------------ 1:11356 1551744 * MethodKlass 2:11356 1435944 * ConstMethodKlass 3:914 973488 * ConstantPoolKlass 4:6717 849032 char [] 5:16987 820072 * SymbolKlass 6:2305 686048 byte [] 7:914 672792 * InstanceKlassKlass 8:857 650312 * ConstantPoolCacheKlass 9:5243 167776 java.lang.String 10:1046 108784 java.lang.Class 11:1400 87576短[] 12:1556 84040 * System ObjArray 13:1037 64584 int [] 14:103 60152 * ObjArrayKlassKlass 15:622 54736 java.lang.reflect.Method 16:1102 49760 java.lang.Object [] 17:937 37480 java.util.TreeMap $ Entry 18:332 27960 java.util.HashMap $ Entry [] 19:579 27792 java.nio.HeapByteBuffer 20:578 27744 java.nio.HeapCharBuffer 21:1021 24504 java.lang.StringBuilder 22:1158 24176 java.lang.Class [] 23:721 23072 java.util.HashMap $ Entry 24:434 20832 java.util.TreeMap 25:689 18936 java.lang.String [] 26:238 17440 java.lang.reflect.Method [] 27:29 16800 * MethodDataKlass 28:204 14688 java.lang.reflect.Field 29:330 13200 java.util.LinkedHashMap $ Entry 30:264 12672 java.util.HashMap ... 585:1 16 java.util.LinkedHashSet 586:1 16 sun.rmi.runtime.NewThreadAction $ 2 587:1 16 java.util.Hashtable $ EmptyIterator 588:1 16 java.util.Collections $ EmptySet 总计:79700 8894800 堆遍历花费了1.288秒。
你总是可以用strace -f -p pid
来查看Java进程正在做什么。 从它的外观(你不能得到没有-F
的jstack
,并且线程5548显示没有调用堆栈并且是IN_VM),看起来线程5548正在花费太多时间来做某事,或者可能处于某种无限循环。
这可能是由于内存不足造成的。 我会尝试两件事情:
通过添加JVM参数在OutOfMemory上启用自动堆转储
-XX:+ HeapDumpOnOutOfMemoryError XX:HeapDumpPath = / tmp
尝试使用JConsole连接到您的JVM,并查看是否有任何不寻常的模式
我会怀疑一个内存问题。 您可能想要使用jstat来观察进程,并在需要杀死进程的时间周围使用jmap进行堆转储。 看看jstat是否表示连续的GC。 另外,您可能需要检查一般系统的运行状况(打开文件描述符,网络等)。 内存将是最简单的,所以我强烈建议从它开始。
当进程正常运行时,通过jstack -F(-F必须存在,它产生不同于jstack的快照)。 线程号不是Thread.id,而是系统号。 5548似乎是在Finalizer和RefCounter之前创建的(它们不是问题的根源),所以它应该是一个GC线程或者一个编译器。
100%可能意味着监视器中的一些错误。 Java(热点)显示器使用非常简单的自旋锁定机制来确保所有权。
当然,附上一个调试器 – GDB来检查进程到底在哪里。
线程5554可能表明你有很多的finalize方法的对象,和/或finalize方法的一些问题。 看这个可能是值得的。
我对jstack并不熟悉,但看起来它输出的信息更少,我更熟悉的线程转储。 尝试获取线程转储可能有用: kill -QUIT java_pid
。 请注意,转储到标准输出可能是控制台或记录文件,具体取决于您的设置。
如果很难确定stdout被引导到哪里,并且假定它正在转到一个文件,那么可以使用最近修改时间来查找候选文件。 这是建议在这篇博客的评论:
你可以在根目录下运行find [2]命令,找出最近x秒内发生了什么变化。 我通常使用find来帮助我访问在过去10分钟内更改的所有日志,例如:find / var / tomcat -mmin -3 -print(打印出在/ var / tomcat下修改的所有文件分钟)。
请注意,如果您正在使用-Xrs
运行JVM,这意味着SIGQUIT
信号处理程序将不会被安装,您将无法使用这种方式请求线程转储。
我遇到了类似的问题,我的JBOSS jvm得到了一个无限循环,最终它得到了OutOfMemory,我不能杀死这个进程而是kill -9。 我怀疑在大多数情况下的内存问题。
以下是一些工具,您可以使用这些工具本地化消耗CPU的进程部分:
perf
/ oprofile
,特别是opannotate
– 非常适合查看地狱代码消耗的周期 strace
, gstack
/ gdb
(正如别人所说) systemtap
功能非常强大,但是与基于ptrace
的工具一样,如果你的问题不涉及系统调用,那么效率就会低很多。