如何找到占用CPU最高的是哪一个线程

当遇到CPU使用率飙升的问题时,本文介绍了一种定位占用CPU最高线程的方法。通过模拟程序创建多个线程,其中有一个线程持续运行导致CPU占用增加。通过`jps`、`top -Hp`、`printf`和`jstack -l`等命令,逐步找出并分析占用CPU最高的线程1。

一、需求分析

 

          经常听到朋友说他们线上CPU有时候突然飙升,想找到是哪个线程导致的,我相信他们肯定有自己的定位方法,今天我要分享的也仅仅是其中一种而已。


二、如何定位

      为了模拟这种现象,写一个程序,先看代码

  

public static void main(String[] args) {
		for(int i=1;i<=3;i++) {
			new Thread() {
	            @Override
				public void run() {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}.start();
		}
		Thread thread = new Thread() {
			    @Override
				public void run() {
					while(true) {
						System.out.println("start foreach");
					}
				}
		};
		thread.setName("线程1");
		thread.start();
	}

      有几个线程,其中,有3个线程什么都没干,仅仅是sleep了,而另外一个单独的线程我让它while(true)一直不停的输出 "start foreach",很明显,这个线程名叫做"线程1"的线程肯定会大量占用CPU,那么在我们事先不知道的情况下,如何把它找出来呢?

      首先将上面代码打成一个jar包,然后直接启动即可,会看看源源不断的输出 start foreach,

      第一步:先jps看一下进程号

  

[root@slave ~]# jps
2942 Jps
2910 jar

      第二步:使用top -Hp  进程号  查看该进程下各个线程占用CPU情况

 

root@slave ~]# top -Hp 2910


top - 01:09:25 up 13 min,  2 users,  load average: 0.11, 0.13, 0.09
Tasks:  14 total,   0 running,  14 sleeping,   0 stopped,   0 zombie
Cpu(s):  5.1%us,  9.1%sy,  0.0%ni, 85.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   3907472k total,  1282472k used,  2625000k free,    69612k buffers
Swap:  1048572k total,        0k used,  1048572k free,   210764k cached

   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                      
  2926 root      20   0 4048m 306m  11m S 11.7  8.0   0:16.61 java                         
  2910 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.04 java                         
  2911 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.46 java                         
  2912 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.00 java                         
  2913 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.00 java                         
  2914 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.00 java                         
  2915 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.00 java                         
  2916 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.00 java                         
  2917 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.00 java                         
  2918 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.00 java                         
  2919 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.45 java                         
  2920 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.13 java                         
  2921 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.00 java                         
  2922 root      20   0 4048m 306m  11m S  0.0  8.0   0:00.27 java 

     从上可以看到,线程号 2926的占用CPU最高。

     第三步: 执行 printf 0x%x 线程号 得到 此线程id的十六进制

  

[root@slave ~]# printf 0x%x 2926
0xb6e

    第四步:使用jstack -l 进程号 输出堆栈信息

  

[root@slave ~]# jstack -l 2910
2018-02-13 01:10:38
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.77-b03 mixed mode):

"Attach Listener" #14 daemon prio=9 os_prio=0 tid=0x00007f796402c000 nid=0xb9b runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x00007f799c009800 nid=0xb5f waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"线程1" #12 prio=5 os_prio=0 tid=0x00007f799c14c800 nid=0xb6e runnable [0x00007f7983849000]
   java.lang.Thread.State: RUNNABLE
        at java.io.FileOutputStream.writeBytes(Native Method)
        at java.io.FileOutputStream.write(FileOutputStream.java:326)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
        - locked <0x000000008001d830> (a java.io.BufferedOutputStream)
        at java.io.PrintStream.write(PrintStream.java:482)
        - locked <0x000000008001d780> (a java.io.PrintStream)
        at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
        at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
        at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
        - locked <0x000000008001da58> (a java.io.OutputStreamWriter)
        at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
        at java.io.PrintStream.newLine(PrintStream.java:546)
        - eliminated <0x000000008001d780> (a java.io.PrintStream)
        at java.io.PrintStream.println(PrintStream.java:807)
        - locked <0x000000008001d780> (a java.io.PrintStream)
        at com.tanjie.java.jvm.JVM如何定位消耗CPU最高的线程$2.run(JVM如何定位消耗CPU最高的线程.java:22)

   Locked ownable synchronizers:
        - None

"Service Thread" #8 daemon prio=9 os_prio=0 tid=0x00007f799c119000 nid=0xb69 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C1 CompilerThread1" #7 daemon prio=9 os_prio=0 tid=0x00007f799c0fc000 nid=0xb68 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C2 CompilerThread0" #6 daemon prio=9 os_prio=0 tid=0x00007f799c0fa000 nid=0xb67 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Signal Dispatcher" #5 daemon prio=9 os_prio=0 tid=0x00007f799c0f7800 nid=0xb66 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Surrogate Locker Thread (Concurrent GC)" #4 daemon prio=9 os_prio=0 tid=0x00007f799c0f6000 nid=0xb65 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f799c0c3800 nid=0xb64 in Object.wait() [0x00007f7983d13000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000080008ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
        - locked <0x0000000080008ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

   Locked ownable synchronizers:
        - None

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f799c0be800 nid=0xb63 in Object.wait() [0x00007f7983d54000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000080006b50> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x0000000080006b50> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

   Locked ownable synchronizers:
        - None

"VM Thread" os_prio=0 tid=0x00007f799c0b7000 nid=0xb62 runnable 

"Gang worker#0 (Parallel GC Threads)" os_prio=0 tid=0x00007f799c01a800 nid=0xb60 runnable 

"Concurrent Mark-Sweep GC Thread" os_prio=0 tid=0x00007f799c03d000 nid=0xb61 runnable 

"VM Periodic Task Thread" os_prio=0 tid=0x00007f799c124800 nid=0xb6a waiting on condition 

JNI global references: 9
    然后找到nid=0xb6e的线程堆栈,即可定位问题。
### 3.1 定位高 CPU 占用线程的方法 在排查高 CPU 占用问题时,可以通过多种工具和方法来识别导致 CPU 负载异常的线程。以下是几种常见且有效的方式: #### 使用 Process Explorer 查看线程的函数调用堆栈 Process Explorer 是 Windows 平台下一款功能强大的系统工具,它可以显示当前运行的进程及其线程的详细信息。通过该工具可以查看线程的函数调用堆栈,从而定位高 CPU 占用的根源。具体操作包括: - 打开 Process Explorer,找到目标进程。 - 右键点击该进程,选择 "Properties"。 - 在 "Threads" 标签页中,可以看到各个线程CPU 占用情况。 - 点击某个线程后,可以查看其调用堆栈,从而判断是哪个函数导致了高 CPU 占用 [^3]。 #### 使用 top + jstack(适用于 Java 应用) 在 Linux 系统中,可以通过 `top` 命令结合 `jstack` 工具来分析 Java 应用中 CPU 占用高的线程- 使用 `top` 命令查看系统中 CPU 占用最高的进程。 - 使用 `top -p <pid> -H` 查看该进程下各个线程CPU 占用情况。 - 获取占用高的线程 ID(TID),将其转换为十六进制。 - 使用 `jstack <pid>` 输出线程堆栈信息,并查找对应 TID 的线程堆栈,分析其执行路径 [^2]。 示例命令如下: ```bash top -p <pid> -H printf "%x\n" <tid> jstack <pid> | grep -A 30 <hex_tid> ``` #### 使用 Clumsy 和 Process Explorer(Windows 平台) Clumsy 是一个用于模拟网络延迟、丢包等网络问题的工具,同时也可以用于分析线程行为。结合 Process Explorer,可以在 Windows 环境下更直观地定位高 CPU 占用问题。例如,在排查某个远程控制软件(如向日葵)的 CPU 占用问题时,通过 Process Explorer 可以快速识别出占用 CPU线程,并进一步分析其调用堆栈 [^3]。 #### 其他工具与方法 对于非 Java 应用,也可以使用 `perf`(Linux)或 `VisualVM`(Java)等性能分析工具进行更深入的剖析。`perf` 可以对进程进行采样,生成火焰图,帮助识别热点函数;`VisualVM` 则提供了图形界面,可以实时查看线程状态、CPU 占用及堆栈信息。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值