我在构建服务器上出现间歇性问题,其中构建中的Java进程无法终止并且似乎永远在运行(使用100%的CPU)(我已经看到它运行了2天以上)周末通常需要大约10分钟)。 kill -9 pid
似乎是阻止这一过程的唯一方法。
我试过在进程上调用kill -QUIT pid
,但它似乎没有产生任何到STDOUT的堆栈跟踪(可能它没有响应信号?)。没有-F force选项的jstack似乎无法连接到正在运行的JVM,但是使用force选项它会产生下面包含的输出。
不幸的是,即使有了这个堆栈跟踪,我也看不到任何明显的进一步调查路径。
据我所知,它显示了两个运行Object.wait的'BLOCKED'线程(它们的堆栈似乎只包含核心Java代码,不包含我们的代码)和第三个没有堆栈输出的'IN_VM'。
我应该采取哪些步骤来收集有关问题原因的更多信息(或者更好的是,我该如何解决?)
$ /opt/jdk1.6.0_29/bin/jstack -l -F 5546 Attaching to process ID 5546, please wait... Debugger attached successfully. Server compiler detected. JVM version is 20.4-b02 Deadlock Detection: No deadlocks found. Finding object size using Printezis bits and skipping over... Thread 5555: (state = BLOCKED) Locked ownable synchronizers: - None Thread 5554: (state = BLOCKED) - java.lang.Object.wait(long) @bci=0 (Interpreted frame) - 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) Locked ownable synchronizers: - None Thread 5553: (state = BLOCKED) - java.lang.Object.wait(long) @bci=0 (Interpreted frame) - java.lang.Object.wait() @bci=2, line=485 (Interpreted frame) - java.lang.ref.Reference$ReferenceHandler.run() @bci=46, line=116 (Interpreted frame) Locked ownable synchronizers: - None Thread 5548: (state = IN_VM) Locked ownable synchronizers: - None
(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
返回...
Attaching to process ID 894, please wait... Debugger attached successfully. Server compiler detected. JVM version is 20.4-b02 Iterating over heap. This may take a while... Finding object size using Printezis bits and skipping over... Finding object size using Printezis bits and skipping over... Object Histogram: num #instances #bytes Class description -------------------------------------------------------------------------- 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 short[] 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 Total : 79700 8894800 Heap traversal took 1.288 seconds.
答案 0 :(得分:3)
您始终可以执行strace -f -p pid
来查看Java进程正在执行的操作。从它的外观(你不能得到jstack
没有-F
,并且线程5548没有显示调用堆栈并且是IN_VM),看起来线程5548花了太多东西去做,或者可能在一些无限循环中。
答案 1 :(得分:2)
这可能是由内存不足引起的。我会尝试两件事:
通过添加JVM参数在OutOfMemory上启用自动堆转储
-XX:+ HeapDumpOnOutOfMemoryError XX:HeapDumpPath = / tmp
尝试使用JConsole连接到JVM并查看是否存在任何异常模式
答案 2 :(得分:2)
我怀疑是内存问题。您可能希望使用jstat观察进程,并在需要终止进程时使用jmap进行堆转储。查看jstat是否表示连续GC。此外,您可能希望检查系统的健康状况(打开文件描述符,网络等)。内存将是最简单的,所以我强烈建议从它开始。
答案 3 :(得分:2)
在进程正常运行时通过jstack -F拍摄快照(-F必须存在,它产生的快照不同于jstack)。线程号不是Thread.id,而是系统一。 5548似乎是在Finalizer和RefCounter之前创建的(它们不是问题的根源),因此它应该是GC线程或某些编译器。
100%可能意味着监视器中存在一些错误。 Java(热点)监视器使用非常简单的自旋锁定机制来确保所有权。
当然,附上一个调试器 - GDB来检查进程的确切位置。
答案 4 :(得分:1)
线程5554可能表示你有很多带有finalize方法的对象,和/或有一个finalize方法的问题。看一下可能是值得的。
我对jstack并不熟悉,但它看起来输出的线程转储的信息较少,我更熟悉。尝试获取线程转储可能很有用:kill -QUIT java_pid
。请注意,转储转到stdout,这可能是控制台或日志文件,具体取决于您的设置。
如果很难确定stdout的定向位置,并假设它将转到文件,则可以使用find
最近的修改时间来识别候选文件。这在this blog post的评论中建议:
你可以在根目录下运行find [2]命令并找出答案 在最后x秒内改变了。我经常用find来帮助我 访问过去10分钟内更改的所有日志,例如:find / var / tomcat -mmin -3 -print(打印出所有修改过的文件 / var / tomcat in hte最后3分钟)。
请注意,如果使用-Xrs
运行JVM,则意味着将不会安装SIGQUIT
信号处理程序,并且您将无法使用请求线程转储的方法。< / p>
答案 5 :(得分:1)
我遇到了类似的问题,我的JBOSS jvm获得了一个无限循环,最终它得到了OutOfMemory,我无法杀死进程但杀了-9。在大多数情况下,我怀疑内存问题。
答案 6 :(得分:0)
以下是一些可用于本地化消耗CPU的进程部分的工具:
perf
/ oprofile
,尤其是opannotate
- 非常适合查看地狱代码消耗周期的内容strace
,gstack
/ gdb
(正如其他人所述)systemtap
非常强大,但与基于ptrace
的工具的某些方式有限(如果您的问题不涉及系统调用,则效果要差得多)。