分析 java 占用 cpu 过高的原因以及 java ScheduledThreadPoolExecutor bug 介绍

近期发现一个java进程的cpu占用接近100%。

开始简单地认为是由于给java分配的内存不足,从而导致频繁GC。

于是首先的处理方式就是直接给该java程序分配更多的内存,然而进程启动没几分钟,cpu占用再次接近100%,看来问题没这么简单。

 

一. 分析 java 占用 cpu 过高的原因

1. 通过top命令直接查到该java进程的进程ID,可以看到进程ID为26260。

-bash-4.2$ top
top - 10:59:04 up 40 days, 19:54,  2 users,  load average: 6.61, 5.06, 5.27
Tasks:  92 total,   1 running,  91 sleeping,   0 stopped,   0 zombie
%Cpu(s): 99.8 us,  0.2 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8010372 total,   640004 free,  5507184 used,  1863184 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  2194908 avail Mem 
 
 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                       
26260 root      20   0 3631872 189640  16676 S  98.3  2.4   5:34.30 java   


2. 再次通过top命令找到该进程中占用cpu较高的线程,该线程的线程ID为26275。

[root@aliyun dancen]# top -H -p 26260
top - 15:03:57 up 40 days, 23:59,  2 users,  load average: 1.08, 1.07, 1.11
Threads:  27 total,   1 running,  26 sleeping,   0 stopped,   0 zombie
%Cpu(s): 53.3 us,  0.0 sy,  0.0 ni, 46.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8010372 total,   447420 free,  5668448 used,  1894504 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  2033320 avail Mem 
 
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                                                        
26275 root      20   0 3678404 326680  17116 R 99.9  4.1 248:24.00 java                                                                                                                                           
26260 root      20   0 3678404 326680  17116 S  0.0  4.1   0:00.00 java                                                                                                                                           
26261 root      20   0 3678404 326680  17116 S  0.0  4.1   0:01.54 java                                                                                                                                           
26262 root      20   0 3678404 326680  17116 S  0.0  4.1   0:00.06 java


3. 通过printf命令得出该线程ID的十六进制表示为66a3。

[root@aliyun dancen]# printf '%x\n' 26275
66a3

4. 使用jstack命令打印该java进程的线程堆栈信息,该命令需要root权限来执行。

[root@aliyun dancen]# jstack 26260 > jstack.log

5. 在jstack.log中找出本地ID,即nid为66a3的线程信息。

[root@aliyun dancen]# cat jstack.log | grep 66a3 -A 10
"pool-3-thread-1" #12 prio=5 os_prio=0 tid=0x00007f0654780000 nid=0x66a3 runnable [0x00007f063c722000]
   java.lang.Thread.State: RUNNABLE
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.poll(ScheduledThreadPoolExecutor.java:809)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)
"Log4j2-TF-5-Scheduled-1" #9 daemon prio=5 os_prio=0 tid=0x00007f06545de800 nid=0x66a2 waiting on condition [0x00007f06441ca000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)

可以看出问题出在ScheduledThreadPoolExecutor这个类的内部类DelayedWorkQueue的poll方法。

二. ScheduledThreadPoolExecutor bug 介绍

从上面可以看到,ScheduledThreadPoolExecutor这个类的内部类DelayedWorkQueue的poll方法消耗了大量的cpu资源。

查看代码中用到ScheduledThreadPoolExecutor的地方:

int corePoolSize = 0;
ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(corePoolSize);

这里声明了一个corePoolSize为0的线程池,这是有点诡异的地方,但是是允许的。

然而,问题就出在这个诡异点,这里触发了java的一个bug:

 

JDK-8129861:

ScheduledThreadPoolExecutor with corePoolSize = 0 causes 100% load on one CPU core

 

该bug即使用corePoolSize为0的ScheduledThreadPoolExecutor,会导致cpu飙升,已在java 9修复。具体描述请参见:

https://bugs.openjdk.java.net/browse/JDK-8129861
————————————————
版权声明:本文为CSDN博主「Dancen」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Dancen/article/details/82968821

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值