Java应用程序CPU负载过高的解决方法

3
今天,我发现我的服务器的CPU负载过高,并且服务器只运行了一个Java应用程序。
以下是我的操作步骤:
  1. 使用top命令查找应用程序的pid。 pid为25713。
  2. 使用top -H -p 25713命令查找使用最多CPU的一些pid。例如25719 tomcat   20   0 10.6g 1.5g  13m R 97.8  4.7 314:35.22 java
  3. 使用jstack -F 25713命令打印dump信息。例如"Gang worker#4 (Parallel GC Threads)" os_prio=0 tid=0x00007f5f10021800 nid=0x6477 runnable
  4. 从dump文件中搜索pid。然后我发现使用大部分CPU的pid都类似于"Gang worker#4 (Parallel GC Threads)" os_prio=0 tid=0x00007f5f10021800 nid=0x6477 runnable
  5. 使用jstack命令后,CPU负载恢复正常!
以下是我的问题:
  1. GC Threads为什么会导致CPU负载过高。
  2. 为什么使用jstack命令后,CPU负载变得正常。
更多日志如下:2015-10-10T10:17:52.019+0800: 71128.973: [GC (Allocation Failure) 2015-10-10T10:17:52.019+0800: 71128.973: [ParNew: 309991K->206K(348416K), 0.0051145 secs] 616178K->306393K(1009920K), 0.0052406 secs] [Times: user=0.09 sys=0.00, real=0.01 secs] 当CPU负载过高时,GC日志停留在[GC (Allocation Failure) 2015-10-10T10:18:10.564+0800: 71147.518: [ParNew:,其他日志都没有。
当我执行jstack命令时,会打印日志。
2015-10-10T10:17:50.757+0800: 53501.137: [GC (Allocation Failure) 2015-10-10T10:17:50.757+0800: 53501.137: [ParNew: 210022K->245K(235968K), 369.6907808 secs] 400188K->1
90410K(1022400K), 369.6909604 secs] [Times: user=3475.15 sys=11.69, real=369.63 secs] 

对于Java 7,关闭并行GC的方法是:-XX:-UseParallelGC - rsutormin
@rsutormin - Voodoo GC调优.... - Stephen C
你使用的操作系统、Java版本和CPU是什么?我理解得对吗,进程在GC中卡住直到你使用jstack -F命令? - the8472
@StephenC - 是的,但我认为这种行为的根本问题在于程序产生了大量的小对象,并以高速将它们丢弃。因此,我认为应该考虑优化相关的重用已经创建的对象,而不是制造新的对象。 - rsutormin
1
很奇怪,如果是futex_wait错误导致的问题,关闭并行GC可能确实可行。 - biziclop
显示剩余3条评论
2个回答

4

我猜测,你可能受到了某些内核版本中存在的futex_wait bug的影响。

更一般地说,jstack -F向进程发送一个信号,这将中断可能正在睡眠的任何线程。因此,也许GC线程只是在等待另一个线程旋转等待,以某种方式错过了唤醒。也就是说,如果它确实被卡在GC中,发送信号可以解决问题,那么这可能指向锁定或内存排序错误,如果不在内核中,则在JVM中。

你可以尝试发送SIGBREAK到进程,看看是否有相同的效果,而不是使用jstack -F


抱歉,我不明白你在说什么。 - the8472
这不是系统错误。如果我在其他服务器上部署应用程序,也会出现同样的问题。所以。 - Dev Zhou
如果您遇到了问题,那么也许您应该查看服务器、JVM版本或JVM标志(-XX:+PrintFlagsFinal)之间是否存在差异。或者,如果您的系统因其他JVM(交换、IO、CPU负载)而经历了负载峰值,则需要进行检查。 - the8472
CentOS 版本为 CentOS release 6.7 (Final) - Dev Zhou
你的内核版本是 2.6.32-504.el6.x86_64,修复程序在 kernel-2.6.32-504.16.2.el6 中(注意 16.2)。因此,是的,你的内核版本在受影响的版本范围内。你只需要阅读我提供链接的邮件列表线程,所有的信息都在那里。 - the8472
显示剩余3条评论

0
GC线程为什么会导致CPU负载过高。
很可能是你的JVM正在运行完整的GC。由于你的JVM可能正在以巨大的堆栈运行(由10.6 GB的内存大小暗示),这将需要很长时间。还有可能是你的系统正在折磨虚拟内存。
使用jstack命令后,为什么CPU恢复正常。
很可能只是巧合。在你运行jstack时,GC已经完成了。

如果您想调查这个问题,我建议您打开垃圾回收日志,并尝试将高CPU负载期间与GC活动相关联。

GC日志应该告诉您的另一件事是,您的Tomcat堆栈是否太满了。如果您的Web应用程序存在内存泄漏,则会导致堆栈填充无法进行垃圾回收的对象。随着时间的推移,这将导致JVM花费越来越多的时间运行GC。如果这是问题所在,那么您需要找到并修复内存泄漏。


最后一次GC日志打印时间为 2015-10-10T10:17:50.757+0800: 53501.137: [GC (Allocation Failure) 2015-10-10T10:17:50.757+0800: 53501.137: [ParNew: 210022K->245K(235968K), 369.6907808 secs] 400188K->190410K(1022400K), 369.6909604 secs] [Times: user=3475.15 sys=11.69, real=369.63 secs] - Dev Zhou
那似乎不像是“完整堆”/“内存泄漏”问题。不过我不是GC调优专家。建议你将更多的GC日志和JVM的GC开关剪切粘贴到问题中...希望有其他人可以帮助你。 - Stephen C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接