执行模拟程序
本文中,我们会通过测试程序模拟java占用CPU过高,然后通过工具排查出原因。环境:jdk8 centos7。
测试程序如下MaxCpuMain.java,模拟了一个高耗CPU线程,5个低耗CPU线程:
import java.util.concurrent.TimeUnit; public class MaxCpuMain { public static void main(String[] args) throws InterruptedException { new Thread(() -> { runMaxCpuCal(); }).start(); for (int i = 0; i < 5; i++) { new Thread(() -> { runMinCpuCal(); }).start(); } while (true) { Thread.sleep(100); } } public static void runMaxCpuCal() { long count = 0; while (true) { count++; double a = Integer.MAX_VALUE + Math.random() * Integer.MAX_VALUE; long b = ("sdf" + a).hashCode(); if (count % (10000 * 10000) == 0) { System.out.println("runMaxCpuCal run" + count); } } } public static void runMinCpuCal() { long count = 0; while (true) { count++; try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if (count % (30) == 0) { System.out.println("runMinCpuCal run" + count); } } } }编译运行程序:
[root@centos-7 test]# javac MaxCpuMain.java [root@centos-7 test]# java MaxCpuMain runMinCpuCal run30 runMinCpuCal run30 runMinCpuCal run30 runMinCpuCal run30 runMinCpuCal run30 runMaxCpuCal run1000000001。TOP确定进程PID
通过top(输入大写P按cpu排序)查占用CPU多的pid;查到进程2505。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2505 root 20 0 2141720 34020 12112 S 98.3 5.8 2:01.56 java 1 root 20 0 128204 4992 3136 S 0.0 0.9 0:01.16 systemd 2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd 3 root 20 0 0 0 0 S 0.0 0.0 0:00.04 ksoftirqd/0 4 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0 5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H 6 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kworker/u64+ 7 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/0 8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh2。TOP确认线程Id
通过进程pid查看占用CPU高的线程, top -Hp 2505,查到2515线程
top -Hp 2505 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2515 root 20 0 2141720 11976 576 S 89.4 2.1 4:28.63 java 2507 root 20 0 2141720 11976 576 R 6.3 2.1 0:14.82 java 2505 root 20 0 2141720 11976 576 S 0.0 2.1 0:00.00 java 2506 root 20 0 2141720 11976 576 S 0.0 2.1 0:00.16 java 2508 root 20 0 2141720 11976 576 S 0.0 2.1 0:00.00 java 2509 root 20 0 2141720 11976 576 S 0.0 2.1 0:00.00 java 2510 root 20 0 2141720 11976 576 S 0.0 2.1 0:00.00 java 2511 root 20 0 2141720 11976 576 S 0.0 2.1 0:00.10 java3。jstack确定代码
首先转换出线程id的16进制
printf '%x' 2515 9d3从线程堆栈中追查是哪个线程,执行jstack2505,并在结果搜索9d3线程。
jstack 2505 太长,只列出部分 "Thread-1" #9 prio=5 os_prio=0 tid=0x00007fc1ac0f8800 nid=0x9d4 waiting on condition [0x00007fc1b0d2a000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at MaxCpuMain.runMinCpuCal(MaxCpuMain.java:37) at MaxCpuMain.lambda$main$1(MaxCpuMain.java:11) at MaxCpuMain$$Lambda$2/303563356.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "Thread-0" #8 prio=5 os_prio=0 tid=0x00007fc1ac0f6800 nid=0x9d3 runnable [0x00007fc1b0e2b000] java.lang.Thread.State: RUNNABLE at sun.misc.FloatingDecimal$BinaryToASCIIBuffer.access$100(FloatingDecimal.java:259) at sun.misc.FloatingDecimal.getBinaryToASCIIConverter(FloatingDecimal.java:1785) at sun.misc.FloatingDecimal.getBinaryToASCIIConverter(FloatingDecimal.java:1738) at sun.misc.FloatingDecimal.appendTo(FloatingDecimal.java:89) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:736) at java.lang.StringBuilder.append(StringBuilder.java:226) at MaxCpuMain.runMaxCpuCal(MaxCpuMain.java:25) at MaxCpuMain.lambda$main$0(MaxCpuMain.java:6) at MaxCpuMain$$Lambda$1/471910020.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fc1ac0b5000 nid=0x9d1 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE从下面的堆栈中查找9d3,可以得知MaxCpuMain.runMaxCpuCal(MaxCpuMain.java:25)是耗费性能的代码。