Java应用如何收集CPU使用率高的问题信息

免责声明: 本文档可能包含第三方产品信息,该信息仅供参考。阿里云对第三方产品的性能、可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。

概述

Java应用程序运行中,会遇到Java应用(JVM)进程CPU使用率高的情况。在这种情况下,Java应用的性能通常会下降,我们可以借助一些工具或命令收集问题信息,进行分析诊断,找到并解决造成Java应用(JVM)进程CPU使用率高的原因。

详细信息

使用edas-agent自带的命令诊断

EDAS为导入ECS集群中的ECS提供了一个可以直接显示出应用进程使用CPU的线程及其StackTrace,可以帮助您快速找到造成应用进程CPU使用率高的问题原因。

使用root用户登录到CPU高的应用进程所在ECS,然后切换到admin用户,执行以下命令,即可查看到应用进程中消耗CPU高的线程(默认前5个)。

说明:执行edas busy-threads -h命令可以查看该命令的具体使用帮助。
edas busy-threads

系统显示类似如下。

09/28/19 22:57:07 [INFO]  EXECUTING: busy-threads
[1] Busy(4.6%) thread(3222/0xc96) stack of java process(3221) under user(admin):
"main" #1 prio=5 os_prio=0 tid=0x00002ab68004e800 nid=0xc96 runnable [0x00002ab67c1df000]
   java.lang.Thread.State: RUNNABLE
    at java.net.PlainSocketImpl.socketAccept(Native Method)
    at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
    at java.net.ServerSocket.implAccept(ServerSocket.java:545)
    at java.net.ServerSocket.accept(ServerSocket.java:513)
    at org.apache.catalina.core.StandardServer.await(StandardServer.java:490)
    at org.apache.catalina.startup.Catalina.await(Catalina.java:819)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:765)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:309)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:443)

[2] Busy(0.9%) thread(2725/0xaa5) stack of java process(2721) under user(admin):
"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x00002ba81004c000 nid=0xaa5 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

[3] Busy(0.0%) thread(3221/0xc95) stack of java process(3221) under user(admin):
[4] Busy(0.0%) thread(2721/0xaa1) stack of java process(2721) under user(admin):

以下是常用的几个参数及选项使用示例:

#每隔2秒执行一次执行结果,共执行5次(每次默认显示应用进程中前5个使用CPU高的线程)。
edas busy-threads 2 5

#显示指定Java进程的前5个使用CPU高的线程,[$JVM_PID]为Java进程的进程号,您可以使用ps -ef |grep java命令查看Java进程的进程号。
edas busy-threads -p [$JVM_PID]

#显示Java进程中前10个使用CPU高的线程。
edas busy-threads -c 10

#显示最近的2秒种内,CPU使用较高的线程。
edas busy-threads --current

使用开源工具诊断

show-busy-java-threads

在非ECS集群的环境中,可以使用show-busy-java-threads开源脚本来找到占用指定进程中排名前几位CPU高的线程。执行以下命令,下载并使用show-busy-java-threads脚本。具体使用方法,请参见show-busy-java-threads

wget --no-check-certificate https://raw.github.com/oldratlee/useful-scripts/release/show-busy-java-threads
chmod +x show-busy-java-threads
./show-busy-java-threads

Arthas

除了该脚本外,还可以使用阿里巴巴的Java问题综合诊断工具Arthas。该工具也可以用来显示指定JVM进程中使用CPU的排名前几位的线程。执行以下命令,显示当前连接的JVM进程中CPU占用排名前3名的线程及StraceTrace信息。

说明:Arthas的下载方法请参见Arthas官网

thread -n 3

系统显示类似如下。

"as-command-execute-daemon" Id=29 cpuUsage=75% RUNNABLE
    at sun.management.ThreadImpl.dumpThreads0(Native Method)
    at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:440)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand$1.action(ThreadCommand.java:58)
    at com.taobao.arthas.core.command.handler.AbstractCommandHandler.execute(AbstractCommandHandler.java:238)
    at com.taobao.arthas.core.command.handler.DefaultCommandHandler.handleCommand(DefaultCommandHandler.java:67)
    at com.taobao.arthas.core.server.ArthasServer$4.run(ArthasServer.java:276)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

    Number of locked synchronizers = 1
    - java.util.concurrent.ThreadPoolExecutor$Worker@6cd0b6f8

"as-session-expire-daemon" Id=25 cpuUsage=24% TIMED_WAITING
    at java.lang.Thread.sleep(Native Method)
    at com.taobao.arthas.core.server.DefaultSessionManager$2.run(DefaultSessionManager.java:85)

"Reference Handler" Id=2 cpuUsage=0% WAITING on java.lang.ref.Reference$Lock@69ba0f27
    at java.lang.Object.wait(Native Method)
    -  waiting on java.lang.ref.Reference$Lock@69ba0f27
    at java.lang.Object.wait(Object.java:503)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)

使用传统方式诊断

在不能连接公网的情况下,您可以使用以下方法来获取CPU占用较高的线程。执行以下命令,收集指定Java进程的信息。

top -Hbp [$JVM_PID] -d 1 -n 1 >> top.[$JVM_PID].txt && jstack [$JVM_PID] >> jstack.[$JVM_PID].txt
  1. 从收集到的top.XXX.txt中找到CPU占用率最高的线程ID(注意top.XXX.txt跟jstack.XXX.txt文件中的线程堆栈信息一一对应)。
  2. 执行以下命令,将找到的线程ID(十进制的数字)转换成十六进制。
    printf %x [$Thread_ID]
    说明:[$Thread_ID]为第1步找到的线程ID。
  3. 用这些转换后的十六进制的线程ID去 jstack.XXX.txt文件中搜索,即可找到对应线程的信息。

适用于

  • 企业级分布式应用服务EDAS