前言

在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)

这个就是我们想找的线程