使用YourKit对应用程序进行了分析,仍然无法确定CPU占用过高的原因。

4

我有一个Java应用程序,大部分时间都在消耗100%的CPU资源(如cacti和top监控所示)。我们启动了YourKit(它确认了CPU资源问题),并将java.net.SocketInputStream.read(byte[], int, int)标识为最大的热点,占据了15%的时间。我认为像SocketInputStream.read这样执行阻塞IO操作的方法,并没有准确地测量CPU时间。

还有6个其他已确定的热点,但它们共计在5%-1%的范围内,总计不到20%的CPU时间。

因此,我知道我有问题,我可以看到问题,YourKit也能看到,但我离确定实际问题还有很远的距离。

我对使用分析器还比较新手,很可能会漏掉一些东西,您有什么建议吗?

编辑:Sean提出了使用系统内置工具的好方法。如果我使用top和shift+h查看线程,则会显示7-15个线程,并且CPU利用率会跳动。我不认为是任何一个线程引起了问题,而是每个线程在某个时候都要执行的一段代码造成的问题。


1
如果在网络中,请考虑带宽延迟。我有一个网络爬虫,50%的时间是打开连接到页面并下载数据。读取速度可能很快,但数据到达的延迟可能达到15%。 - Renato Dinhani
对,这就是我在谈论阻塞IO的意思。我相信分析器只是展示了墙钟时间而不是实际CPU时间。 - DanInDC
尝试随机暂停 - Mike Dunlavey
5个回答

3
我建议在Solaris上运行此操作,如果可以的话。如果您没有Solaris,请考虑使用安装Open Solaris的虚拟机。
Solaris提供了一个称为prstat的工具。
Prstat的工作方式与大多数人熟悉的top类似。重要的区别是prstat可以为您分解进程并显示每个进程中的每个线程。
对于您的情况,使用如下命令 prstat -L 0 1
配合线程转储(最好在脚本中完成),您可以将LWPID匹配在一起,找出哪个线程是CPU占用最高的。
以下是一个功能示例(我创建了一个小应用程序进行循环)。
标准Top将显示以下内容。
 PID USERNAME NLWP PRI NICE  SIZE   RES STATE    TIME    CPU COMMAND
  924 username   10  59    0   31M   11M run      0:53 36.02% java

然后使用 prstat 命令如下

 prstat -L 0 1 | grep java > /export/home/username/Desktop/output.txt

并且从prstat输出

PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/LWPID    
924 username   31M   10M run     30    0   0:00:09  35% java/10
924 username   31M   10M sleep   59    0   0:00:00 0.8% java/3
924 username   31M   10M sleep   59    0   0:00:00 0.6% java/2
924 username   31M   10M sleep   59    0   0:00:00 0.3% java/1

这看起来与顶部区别不大,但是如果你注意数据的右侧,PROCESS/LWPID会告诉你消耗CPU的Java进程中确切的线程。使用轻量级进程ID(LWPID)为10的线程正在消耗35%的CPU。如我之前所述,如果你将其与线程转储配对使用,可以找到确切的线程。对于我的情况,这是线程转储的相关部分。
"Thread-0" prio=3 tid=0x08173800 nid=0xa runnable [0xc60fc000..0xc60fcae0]
   java.lang.Thread.State: RUNNABLE
    at java.util.Random.next(Random.java:139)
    at java.util.Random.nextInt(Random.java:189)
    at ConsumerThread.run(ConsumerThread.java:13)

在线程的顶部一行,nid 可以与 LWPID 匹配。nid=0xa (当从十六进制转换为十进制时为10)。
如果您可以将 prstat 和 thread dump 命令放到一个脚本中,并在高 CPU 使用率期间运行 4-5 次,您将开始看到模式并能够确定高 CPU 的原因。
在我的经验中,我曾经看到长时间运行的 gc 时间或 LDAP 连接配置错误导致了这个结果。祝玩得开心 :)

很棒的信息。在Linux下有非常类似的东西。http://blogs.manageengine.com/appmanager/2011/02/09/identify-java-code-consuming-high-cpu-in-linux-linking-jvm-thread-and-linux-pid - DanInDC
我不知道Linux中的shift+h选项。等我有空了,我得去看一下。 - Sean

2

1

jvisualvm分析器是一种方便的比较工具;它已经包含在JDK中。


1

我会打开内存跟踪和CPU分析,并再次查看CPU分析器。这将显示不同的优化区域。

当你说它使用了100%的CPU时,你能否查看它是在用户空间还是系统/内核空间中。例如:top。分析器不会显示在内核空间中使用的CPU。

你有多少个线程?如果你有足够的空闲线程,你可以在它们之间切换,从而拥有超过100%的CPU。(你必须有成千上万个线程)

像其他类似的答案一样,很可能你的应用程序有太多的开销,例如读取套接字、在线程之间交换、执行GC,以至于它没有做太多真正的工作。分析器在捕捉开销方面并不是很好。


YourKit显示了69个峰值线程,总共创建了大约11,000个线程,持续时间约为1.5小时。这是一个进行大量异步操作的网络爬虫,所以这是可以预料的。 - DanInDC
Cacti 表明,在峰值 CPU 利用率时,用户空间占用约 85%,其余部分均匀分配在系统和内核之间。 - DanInDC
如果我在我的机器上创建了10K个线程,这些线程只是在循环中睡眠1秒钟,它将消耗大约一个逻辑线程的100%(我有8个)。如果您无法空出此CPU,则可以使用选择器模型。您可以为每个核心创建一个线程,并在这些选择器之间分割连接。 - Peter Lawrey

0

有一个可能的原因是你的JVM内存不够,所以它会不停地进行垃圾回收。


YourKit显示,在1.5小时内,它在垃圾回收中总共花费了21秒的时间,包括次要和主要的回收。 - DanInDC

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