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


我在构建服务器上有一个间歇性问题,其中构建中的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
正在处理ID 5546,请等待......
调试器连接成功。
服务器编译检测到
JVM版本是20.4-b02
死锁检测:

没有找到死锁。

使用Printezis位查找对象大小并跳过...
线程5555 :( state = BLOCKED)

锁定可拥有的同步器:
    - 没有

线程5554 :(状态=阻塞)
  -  java.lang.Object.wait(long)@ bci = 0(解释框架)
  -  java.lang.ref.ReferenceQueue.remove(long)@ bci = 44,line = 118(解释框)
  -  java.lang.ref.ReferenceQueue.remove()@ bci = 2,line = 134(解释框架)
  -  java.lang.ref.Finalizer $ FinalizerThread.run()@ bci = 3,line = 159(解释框架)

锁定可拥有的同步器:
    - 没有

线程5553 :(状态=阻塞)
  -  java.lang.Object.wait(long)@ bci = 0(解释框架)
  -  java.lang.Object.wait()@ bci = 2,line = 485(解释框架)
  -  java.lang.ref.Reference $ ReferenceHandler.run()@ bci = 46,line = 116(解释框架)

锁定可拥有的同步器:
    - 没有

线程5548:(state = IN_VM)

锁定可拥有的同步器:
    - 没有

(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,请等待......
调试器连接成功。
服务器编译检测到
JVM版本是20.4-b02
迭代堆。可能还要等一下...
使用Printezis位查找对象大小并跳过...
使用Printezis位查找对象大小并跳过...
物体直方图:

num #instances #bytes类描述
-------------------------------------------------- ------------------------
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 *系统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秒。

9180
2017-11-08 04:40


起源

您可以尝试在运行服务器的控制台上按[CTRL] + [SYSREQ]。这会给你一个线程转储,它会像它获得的那样冗长。 - JimmyB


答案:


你总能做到 strace -f -p pid 看看Java进程在做什么。从它的外观(你不能得到一个 jstack 无 -F,并且线程5548显示没有调用堆栈并且是IN_VM),看起来线程5548做了太多事情,或者可能处于某种无限循环中。


3
2017-11-15 19:19



运行 strace -f -p 894 我得到一堆线条说 [pid 900] sched_yield() = 0...有趣... - Matt Sheppard
@MattSheppard:从这一点来说,我会遵循最好的建议并得到一些回溯。如果你的系统有 pstack,干脆做 pstack <pid>。否则你必须这样做 gdb /path/to/java <pid> 并从gdb bt 和 quit。 - ninjalj
我会在下次发生时尝试。 - Matt Sheppard


这可能是由于内存不足造成的。我会尝试两件事:

  • 通过添加JDBC参数在OutOfMemory上启用自动堆转储

    -XX:+ HeapDumpOnOutOfMemoryError XX:HeapDumpPath = / tmp

  • 尝试使用JConsole连接到JVM,看看是否有任何异常模式


2
2017-11-14 09:45



不幸的是,jconsole似乎没有连接到它(如果我远程尝试,只是超时,如果我从服务器运行它,相关的PID在列表中显示为灰色)。我将看到如何将这些参数提供给正确的JVM调用。 - Matt Sheppard


我怀疑是内存问题。您可能希望使用jstat观察进程,并在需要终止进程时使用jmap进行堆转储。查看jstat是否表示连续GC。此外,您可能希望检查系统的健康状况(打开文件描述符,网络等)。内存将是最简单的,所以我强烈建议从它开始。


2
2017-11-15 18:04



不幸的是,jstat给了我这个消息 Could not synchronize with target。 jmap似乎能够产生一些信息(我将更新上面的问题)。 - Matt Sheppard
我似乎能够得到一堆堆 jmap -F -dump:format=b,file=heap.bin 894 除了上面的直方图,但我还不确定该如何处理它。 - Matt Sheppard
你可以使用eclipse MAT查看堆转储。寻找泄密嫌疑人。然而,奇怪的是你无法使用jstat。你用的是什么命令? - aishwarya


通过jstack -F正常运行进程时拍摄快照(-F必须存在,它产生的快照不同于jstack)。线程号不是Thread.id,而是系统一。 5548似乎是在Finalizer和RefCounter之前创建的(它们不是问题的根源),因此它应该是GC线程或某些编译器线程。

100%可能意味着监视器中的一些错误。 Java(热点)监视器使用非常简单的旋转锁定机制来确保所有权。

当然,附加一个调试器 - GDB来检查进程的确切位置。


2
2017-11-10 16:11





线程5554可能表示您有很多具有finalize方法的对象,和/或finalize方法的一些问题。看看这个可能是值得的。

我不熟悉jstack,但看起来它输出的线程转储的信息越少,我就越熟悉了。尝试获取线程转储可能很有用: kill -QUIT java_pid。请注意,转储将转到stdout,这可能是控制台或日志文件,具体取决于您的设置。

如果很难确定stdout被定向到哪里,并假设它将转到文件,你可以使用 find 通过最近的修改时间来识别候选文件。这在评论中提出 这篇博文

你可以在你的根目录下运行find [2]命令并找出它   在最后x秒内改变了。我经常用find来帮助我   访问过去10分钟内更改的所有日志,例如:find   / var / tomcat -mmin -3 -print(打印出所有修改过的文件   / var / tomcat in hte最后3分钟)。

请注意,如果您正在运行JVM -Xrs,这意味着 SIGQUIT 将不会安装信号处理程序,您将无法使用请求线程转储的方法。


1
2017-11-16 15:53



5554是终结者,除非有最终确定的内容,否则应该停放。它不应该阻止该过程终止。 kill -QUIT是一个非常好的主意,因为它可以帮助你弄清楚5555的情况,这看起来更像是罪魁祸首。 - philwb
我试试看 kill -QUIT 下次问题发生时,看看是否提供了更多信息,谢谢。我认为可能会有一些自定义终结器,但他们应该做的就是关闭打开的文件。我想这是快速而相当安全的,但也许不是...... - Matt Sheppard
我认为@philwb是对的。终结器可能没有错 - 终结器线程正在等待某些事情要做,而不是在工作时被阻止。 - sudocode
kill -QUIT似乎没有任何影响(即没有输出到进程STDOUT的文件)。也许它没有响应信号(kill -9似乎是唯一会阻止它的东西)。 - Matt Sheppard
你是这个过程的所有者吗? - wannik


我遇到了类似的问题,我的JBOSS jvm获得了一个无限循环,最终它得到了OutOfMemory,我无法杀死进程但杀了-9。在大多数情况下我怀疑是内存问题。


1
2017-11-17 03:37





以下是一些可用于本地化消耗CPU的进程部分的工具:

  • perf/ oprofile特别是 opannotate  - 很高兴看到地狱代码消耗周期
  • stracegstack/ gdb (正如其他人所说)
  • systemtap 是非常强大的,但在某些方面受到限制 ptrace 基于工具(如果您的问题不涉及系统调用,则效果要差得多)。

0