前言
在Java应用程序中,高CPU负载是一个常见的问题,通常会导致应用程序的性能下降。在本文中,我们将介绍如何使用JStack工具来检测和分析高CPU负载问题。我们将讨论JStack的工作原理,以及如何使用它来检测和定位高负载线程。我们还将提供一些最佳实践和建议,以帮助您快速解决CPU负载问题,提高应用程序的性能。
什么是JStack
- JStack是Java虚拟机(JVM)自带的一个命令行工具,用于生成Java线程的堆栈跟踪信息。它可以帮助开发人员诊断和调试Java应用程序中的线程问题,例如死锁和线程阻塞等。
- 使用JStack,可以捕获正在运行的Java应用程序的线程堆栈跟踪信息,并将其输出到控制台或文件中。这些信息包括每个线程的状态、调用堆栈和锁信息,这些信息有助于诊断问题并查找代码中的错误。
- 线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
如何找出高负载Java线程
步骤一:查看CPU占用高进程
输入命令
top
输出结果
Tasks: 3 total, 1 running, 2 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.1 us, 1.1 sy, 0.0 ni, 93.7 id, 2.9 wa, 0.0 hi, 0.3 si, 0.0 st
KiB Mem : 32675864 total, 10846008 free, 2076000 used, 19753856 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 30297432 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 2705876 216868 13292 S 7.6 0.7 65:31.27 java
1238 root 20 0 15120 3440 3060 S 0.0 0.0 0:00.01 bash
1252 root 20 0 59488 4404 3788 R 0.0 0.0 0:00.00 top
步骤二:查看CPU占用高线程
输入命令
top -H -p 1
输出结果
top - 09:44:41 up 8 days, 14 min, 0 users, load average: 0.65, 0.82, 0.99
Threads: 110 total, 0 running, 110 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.5 us, 1.6 sy, 0.0 ni, 94.3 id, 1.6 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 32675864 total, 10814188 free, 2072764 used, 19788912 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 30300652 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
58 root 20 0 2705876 216868 13292 S 6.7 0.7 4:11.22 java
59 root 20 0 2705876 216868 13292 S 6.7 0.7 6:40.24 java
111 root 20 0 2705876 216868 13292 S 6.7 0.7 6:03.95 java
1 root 20 0 2705876 216868 13292 S 0.0 0.7 0:00.01 java
6 root 20 0 2705876 216868 13292 S 0.0 0.7 0:04.97 java
7 root 20 0 2705876 216868 13292 S 0.0 0.7 0:42.67 java
8 root 20 0 2705876 216868 13292 S 0.0 0.7 0:42.69 java
9 root 20 0 2705876 216868 13292 S 0.0 0.7 0:05.59 java
10 root 20 0 2705876 216868 13292 S 0.0 0.7 0:00.00 java
11 root 20 0 2705876 216868 13292 S 0.0 0.7 0:00.01 java
12 root 20 0 2705876 216868 13292 S 0.0 0.7 0:00.21 java
13 root 20 0 2705876 216868 13292 S 0.0 0.7 0:19.79 java
步骤三:转换线程ID
输入命令
#转换成16进制
printf "%x\n" 58
输出结果
3a
步骤四:定位CPU占用线程
输入命令
jstack 1 | grep 3a -A 50
说明:
- 1:1为进程ID
- 3a:16进程的线程ID
- -A 50:后50行
输出结果
"nioEventLoopGroup-4-1" #48 prio=10 os_prio=0 tid=0x5b320000 nid=0x3a runnable [0x56e5b000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x68c05ab8> (a io.netty.channel.nio.SelectedSelectionKeySet)
- locked <0x68c05ac8> (a java.util.Collections$UnmodifiableSet)
- locked <0x68c05a78> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:101)
at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:68)
at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:805)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:457)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
这个就是我们想找的线程