用jstack命令分析CPU占用率高的问题

环境

  • Ubuntu 22.04
  • JDK 17.0.3.1

简介

The jstack command prints Java stack traces of Java threads for a specified Java process

用法: jstack [-l][-e]

选项:

  • -l : long listing. Prints additional information about locks
  • -e : extended listing. Prints additional information about threads

测试

下面的Java代码:

package pkg1;

public class Test0608 {
	public static void main(String[] args) {
		Thread.currentThread().setName("MyThread1");
		int i = 0;
		while(true) {
			i++;
			i--;
			i++;
			i--;
			if (i > 100)
				i = 0;
		}
	}
}

这是一个死循环的程序。

注: Test0608.java 文件位于 pkg1 目录下。当前位于 pkg1 目录下。

  • 在命令行下编译: javac pkg1/Test0608.java
  • 在命令行下运行: java pkg1.Test0608 (可以加上 -cp .

运行程序后,陷入死循环,CPU占用率就会飙升。

使用 top 命令查看进程(按下 P 键,按CPU排序,按下 M 键,则按内存排序)

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                      
  27139 ding      20   0 4825108  31552  24268 S 100.3   0.4   0:14.44 java                         
   1916 ding      20   0 4412772 283628 131928 S   1.3   3.5   1:30.23 gnome-shell                  
   1179 mysql     20   0 2374480 395512  35136 S   0.3   4.9   4:03.78 mysqld       

可见,进程 27139 的CPU使用率最高,达到了100%。

可以使用 ps 查看该进程:

ps -ef | grep 27139   
ding       27139    3096 99 20:42 pts/0    00:01:06 java pkg1.Test0608
ding       27189   26323  0 20:43 pts/1    00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox 27139

如果是Java进程,也可以使用 jps 命令来查看:

jps
27200 Jps
27139 Test0608

定位到进程号以后,接下来用 top -H -p 来查看其线程:

top -H -p 27139

top - 20:44:14 up  6:16,  1 user,  load average: 1.23, 0.63, 0.47
Threads:  19 total,   1 running,  18 sleeping,   0 stopped,   0 zombie
%Cpu(s): 12.6 us,  0.2 sy,  0.0 ni, 87.1 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   7914.8 total,   1831.0 free,   2691.5 used,   3392.3 buff/cache
MiB Swap:   2048.0 total,   2045.0 free,      3.0 used.   4896.5 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                      
  27140 ding      20   0 4825108  31552  24268 R  99.7   0.4   2:11.07 MyThread1                    
  27139 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 java                         
  27141 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 GC Thread#0                  
  27142 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 G1 Main Marker               
  27143 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 G1 Conc#0                    
  27144 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 G1 Refine#0                  
  27145 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.01 G1 Service                   
  27146 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 VM Thread                    
  27147 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 Reference Handl              
  27148 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 Finalizer                    
  27149 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 Signal Dispatch              
  27150 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 Service Thread               
  27151 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 Monitor Deflati              
  27152 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 C2 CompilerThre              
  27153 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 C1 CompilerThre              
  27154 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 Sweeper thread               
  27155 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 Notification Th              
  27156 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.07 VM Periodic Tas              
  27157 ding      20   0 4825108  31552  24268 S   0.0   0.4   0:00.00 Common-Cleaner

可见,线程 27140 占用了100%的CPU。其线程名为 MyThread1 (所运行Java程序的主线程)。

获取 27140 的16进制值:

printf "0x%x\n" 27140
0x6a04

这是为了接下来方便查找。

现在,已经知道了CPU占用率高的进程和线程,终于轮到jstack出场了。

jstack -l 27139 
2023-06-08 20:44:52
Full thread dump Java HotSpot(TM) 64-Bit Server VM (17.0.3.1+2-LTS-6 mixed mode, sharing):

Threads class SMR info:
_java_thread_list=0x00007f3e98002010, length=12, elements={
0x00007f3ee0023bb0, 0x00007f3ee0139080, 0x00007f3ee013a460, 0x00007f3ee01406a0,
0x00007f3ee0141a50, 0x00007f3ee0142e60, 0x00007f3ee0144810, 0x00007f3ee0145d40,
0x00007f3ee014f1a0, 0x00007f3ee01568d0, 0x00007f3ee0159fd0, 0x00007f3e98001090
}

"MyThread1" #1 prio=5 os_prio=0 cpu=169036.84ms elapsed=169.11s tid=0x00007f3ee0023bb0 nid=0x6a04 runnable  [0x00007f3ee5dfd000]
   java.lang.Thread.State: RUNNABLE
	at pkg1.Test0608.main(Test0608.java:12)

   Locked ownable synchronizers:
	- None

"Reference Handler" #2 daemon prio=10 os_prio=0 cpu=0.15ms elapsed=169.10s tid=0x00007f3ee0139080 nid=0x6a0b waiting on condition  [0x00007f3ec47fe000]
   java.lang.Thread.State: RUNNABLE
	at java.lang.ref.Reference.waitForReferencePendingList(java.base@17.0.3.1/Native Method)
	at java.lang.ref.Reference.processPendingReferences(java.base@17.0.3.1/Reference.java:253)
	at java.lang.ref.Reference$ReferenceHandler.run(java.base@17.0.3.1/Reference.java:215)

   Locked ownable synchronizers:
	- None
......

下面还有很长。为了方便,我们可以用线程名 MyThread1 或者线程号 0x6a04 来查找。最终找到这一段:

"MyThread1" #1 prio=5 os_prio=0 cpu=169036.84ms elapsed=169.11s tid=0x00007f3ee0023bb0 nid=0x6a04 runnable  [0x00007f3ee5dfd000]
   java.lang.Thread.State: RUNNABLE
	at pkg1.Test0608.main(Test0608.java:12)

   Locked ownable synchronizers:
	- None

这是当前线程状态,可见信息很齐全,有包名,类名,方法名,当前运行的代码行(本例中是第12行)。

可以多运行几次 jstack 命令,如果当前运行的代码行都相同,则这块的代码很可能有问题。对于本例,检查代码第12行,就会很容易发现这里有一个死循环。

参考

  • https://www.oracle.com/java/technologies/javase/jstack.html
  • https://blog.csdn.net/u014163312/article/details/124248558

你可能感兴趣的:(Java,java)