java - 挂起的 JVM 占用 100% CPU

标签 java

我们有一个在 Linux 32 位 (CentOS) 上的 Sun JRE 6u20 上运行的 JAVA 服务器。我们使用带有 CMS 收集器的服务器热点和以下选项(我只提供了相关的选项):

-Xmx896m -Xss128k -XX:NewSize=384M -XX:MaxPermSize=96m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC

有时,在运行一段时间后,JVM 似乎进入挂起状态,即使我们没有向应用程序发出任何请求,CPU 仍继续以 100% 的速度旋转(我们有 8 个逻辑 CPU,所以看起来只有一个 CPU 进行旋转)。 在此状态下,JVM 不响应 SIGHUP 信号 (kill -3),我们无法使用 jstack 正常连接到它。我们可以使用“jstack -F”连接,但输出不可靠(我们可以从 JStack 看到很多 NullPointerExceptions,显然是因为它无法“遍历”某些堆栈)。所以“jstack -F”输出似乎没有用。

不过,我们已经从“gdb”运行了一个堆栈转储,并且我们能够将旋转 CPU 的线程 ID(我们发现使用“top”和每线程 View - “H”选项)与出现在 gdb 结果中的线程堆栈,它是这样的:

Thread 443 (Thread 0x7e5b90 (LWP 26310)):
#0  0x0115ebd3 in CompactibleFreeListSpace::block_size(HeapWord const*) const () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#1  0x01160ff9 in CompactibleFreeListSpace::prepare_for_compaction(CompactPoint*) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#2  0x0123456c in Generation::prepare_for_compaction(CompactPoint*) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#3  0x01229b2c in GenCollectedHeap::prepare_for_compaction() () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#4  0x0122a7fc in GenMarkSweep::invoke_at_safepoint(int, ReferenceProcessor*, bool) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#5  0x01186024 in CMSCollector::do_compaction_work(bool) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#6  0x011859ee in CMSCollector::acquire_control_and_collect(bool, bool) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#7  0x01185705 in ConcurrentMarkSweepGeneration::collect(bool, bool, unsigned int, bool) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#8  0x01227f53 in GenCollectedHeap::do_collection(bool, bool, unsigned int, bool, int) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#9  0x0115c7b5 in GenCollectorPolicy::satisfy_failed_allocation(unsigned int, bool) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#10 0x0122859c in GenCollectedHeap::satisfy_failed_allocation(unsigned int, bool) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#11 0x0158a8ce in VM_GenCollectForAllocation::doit() () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#12 0x015987e6 in VM_Operation::evaluate() () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#13 0x01597c93 in VMThread::evaluate_operation(VM_Operation*) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#14 0x01597f0f in VMThread::loop() () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#15 0x015979f0 in VMThread::run() () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#16 0x0145c24e in java_start(Thread*) () from /usr/java/jdk1.6.0_20/jre/lib/i386/server/libjvm.so
#17 0x00ccd46b in start_thread () from /lib/libpthread.so.0
#18 0x00bc2dbe in clone () from /lib/libc.so.6

似乎 JVM 线程在执行一些与 CMS 相关的工作时正在旋转。 我们已经检查了盒子上的内存使用情况,似乎有足够的可用内存并且系统没有交换。 有没有人遇到过这种情况?它看起来像 JVM 错误吗?

更新

我已经获得了有关此问题的更多信息(它在运行超过 7 天的服务器上再次发生)。 当 JVM 进入“挂起”状态时,它会保持这种状态 2 小时,直到服务器被手动重启。我们已经获得了进程的核心转储和 gc 日志。我们也尝试获取堆转储,但“jmap”失败了。我们尝试使用 jmap -F 但在程序异常中止之前只写入了一个 4Mb 的文件(关于内存位置无法访问的问题)。 到目前为止,我认为最有趣的信息来自 gc 日志。似乎 GC 日志记录也停止了(可能是在 VM 线程进入长循环时):

657501.199: [Full GC (System) 657501.199: [CMS: 400352K->313412K(524288K), 2.4024120 secs] 660634K->313412K(878208K), [CMS Perm : 29455K->29320K(68568K)], 2.4026470 secs] [Times: user=2.39 sys=0.01, real=2.40 secs] 
657513.941: [GC 657513.941: [ParNew: 314624K->13999K(353920K), 0.0228180 secs] 628036K->327412K(878208K), 0.0230510 secs] [Times: user=0.08 sys=0.00, real=0.02 secs] 
657523.772: [GC 657523.772: [ParNew: 328623K->17110K(353920K), 0.0244910 secs] 642036K->330523K(878208K), 0.0247140 secs] [Times: user=0.08 sys=0.00, real=0.02 secs] 
657535.473: [GC 657535.473: [ParNew: 331734K->20282K(353920K), 0.0259480 secs] 645147K->333695K(878208K), 0.0261670 secs] [Times: user=0.11 sys=0.00, real=0.02 secs] 
....
....
688346.765: [GC [1 CMS-initial-mark: 485248K(524288K)] 515694K(878208K), 0.0343730 secs] [Times: user=0.03 sys=0.00, real=0.04 secs] 
688346.800: [CMS-concurrent-mark-start]
688347.964: [CMS-concurrent-mark: 1.083/1.164 secs] [Times: user=2.52 sys=0.09, real=1.16 secs] 
688347.964: [CMS-concurrent-preclean-start]
688347.969: [CMS-concurrent-preclean: 0.004/0.005 secs] [Times: user=0.00 sys=0.01, real=0.01 secs] 
688347.969: [CMS-concurrent-abortable-preclean-start]
 CMS: abort preclean due to time 688352.986: [CMS-concurrent-abortable-preclean: 2.351/5.017 secs] [Times: user=3.83 sys=0.38, real=5.01 secs] 
688352.987: [GC[YG occupancy: 297806 K (353920 K)]688352.987: [Rescan (parallel) , 0.1815250 secs]688353.169: [weak refs processing, 0.0312660 secs] [1 CMS-remark: 485248K(524288K)] 783055K(878208K), 0.2131580 secs] [Times: user=1.13 sys
=0.00, real=0.22 secs] 
688353.201: [CMS-concurrent-sweep-start]
688353.903: [CMS-concurrent-sweep: 0.660/0.702 secs] [Times: user=0.91 sys=0.07, real=0.70 secs] 
688353.903: [CMS-concurrent-reset-start]
688353.912: [CMS-concurrent-reset: 0.008/0.008 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
688354.243: [GC 688354.243: [ParNew: 344928K->30151K(353920K), 0.0305020 secs] 681955K->368044K(878208K), 0.0308880 secs] [Times: user=0.15 sys=0.00, real=0.03 secs]
....
....
688943.029: [GC 688943.029: [ParNew: 336531K->17143K(353920K), 0.0237360 secs] 813250K->494327K(878208K), 0.0241260 secs] [Times: user=0.10 sys=0.00, real=0.03 secs] 
688950.620: [GC 688950.620: [ParNew: 331767K->22442K(353920K), 0.0344110 secs] 808951K->499996K(878208K), 0.0347690 secs] [Times: user=0.11 sys=0.00, real=0.04 secs] 
688956.596: [GC 688956.596: [ParNew: 337064K->37809K(353920K), 0.0488170 secs] 814618K->515896K(878208K), 0.0491550 secs] [Times: user=0.18 sys=0.04, real=0.05 secs] 
688961.470: [GC 688961.471: [ParNew (promotion failed): 352433K->332183K(353920K), 0.1862520 secs]688961.657: [CMS

我怀疑这个问题与日志中的最后一行有关(我添加了一些“....”以跳过一些不感兴趣的行)。 服务器保持挂起状态 2 小时(可能是在尝试 GC 和压缩老年代)这一事实对我来说似乎很奇怪。此外,gc 日志随该消息突然停止,不再打印任何其他内容,可能是因为 VM 线程进入某种无限循环(或需要 2 小时以上的时间)。

最佳答案

看起来像是 GC 调优问题,可能是由内存泄漏引发的。我建议您添加打开 GC 日志记录的 JVM 参数,看看是否能给您带来任何见解。

Does it look like a JVM bug?

对我来说不是。

当您用完堆空间(例如,由于存储泄漏)时,通常会看到 GC 运行得越来越频繁,并且使用越来越大的 CPU 百分比。您可以设置一些 JVM 参数来避免这种情况,但它们会导致您的 JVM 因 OOME 更快崩溃。 (这听起来可能很奇怪,但在很多情况下这是一件好事,因为它减少了系统重新启动并能够再次正常运行的时间。)

关于java - 挂起的 JVM 占用 100% CPU,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4884630/

相关文章:

java - 为什么我们在 java 中有 protected 访问说明符?

java - 在 tomcat 类路径中包含 web-inf/lib 中的目录

java - Ivy 间接依赖项列出两次

java - Android Facebook SDK ShareDialog 不返回回调

java - org.apache.commons.codec.DecoderException : Odd number of characters

应用程序构造函数中的 JavaFX 异常(未找到此类方法异常)

java - ListView header 未按预期工作?

java - WildFly RestEasy 版本困惑

java - Spring 3 SimpleMappingExceptionResolver warnLogCategory log4j

java - 部署 .war : Error occurred during deployment: Exception while preparing the app : Invalid resource 时 Glassfish 服务器错误