为什么这个Java进程无法终止?

时间:2011-11-08 04:40:28

标签: java linux debugging

我在构建服务器上出现间歇性问题,其中构建中的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.

7 个答案:

答案 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 - 非常适合查看地狱代码消耗周期的内容
  • stracegstack / gdb(正如其他人所述)
  • systemtap非常强大,但与基于ptrace的工具的某些方式有限(如果您的问题不涉及系统调用,则效果要差得多)。