Java应用程序运行中,会遇到Java应用(JVM)进程CPU使用率高的情况。在这种情况下,Java应用的性能通常会下降,我们可以借助一些工具或命令收集问题信息,进行分析诊断,找到并解决造成Java应用(JVM)进程CPU使用率高的原因。
重要 本文档可能包含第三方产品信息,该信息仅供参考。阿里云对第三方产品的性能、可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。
使用edas-agent自带的命令诊断
EDAS为导入ECS集群中的ECS提供了一个可以直接显示出应用进程使用CPU的线程及其StackTrace,可以帮助您快速找到造成应用进程CPU使用率高的问题原因。
通过SSH登录到CPU高的应用进程所在的ECS,执行su - admin命令切换到admin账号后,然后执行以下命令,即可查看到应用进程中消耗CPU高的线程(默认前5个)。
edas busy-threads
说明 执行edas busy-threads -h命令可以查看该命令的具体使用帮助。
系统显示类似如下。
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
- 从收集到的top.XXX.txt中找到CPU占用率最高的线程ID(注意top.XXX.txt跟jstack.XXX.txt文件中的线程堆栈信息一一对应)。
- 执行以下命令,将找到的线程ID(十进制的数字)转换成十六进制。
printf %x [$Thread_ID]
说明 [$Thread_ID]为第1步找到的线程ID。 - 用这些转换后的十六进制的线程ID去jstack.XXX.txt文件中搜索,即可找到对应线程的信息。
文档内容是否对您有帮助?