如果可以的话,我建议在 Solaris 机器上运行它。如果您没有 Solaris 机器,请考虑设置一个运行 Open Solaris 的虚拟机。
Solaris 提供了一个名为prstat的工具
prstat 的工作方式与大多数人熟悉的 top 非常相似。重要的区别是 prstat 可以为您分解进程并显示进程中的每个线程。
对于您的情况,用法是 prstat -L 0 1
与线程转储配对(首选在脚本中执行此操作),您可以将 LWPID 匹配在一起,以准确找到哪个线程是 CPU hog。
这是一个功能示例(我创建了一个小应用程序,用于 poc 的大循环)
Standard 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 会告诉您 java 进程中消耗 CPU 的确切线程。以轻量级进程 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 和线程转储命令放在脚本中并在 CPU 使用率较高的情况下运行 4-5 次,您将开始看到模式并能够通过这种方式确定导致 CPU 使用率过高的原因。
在我的时代,我已经看到了从长时间运行 gc 时间到错误配置 LDAP 连接的结果。玩得开心 :)