正如我们在评论中所指出的,占用CPU的线程在以下堆栈中处于繁忙状态:
"pool-9-thread-1" #49 prio=5 os_prio=0 tid=0x00007ffd508e8000 nid=0x3a0c runnable [0x00007ffd188b6000]
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:745)
我成功地通过创建一个 ScheduledThreadPoolExecutor
,将其配置为允许核心线程超时,并调度大量重复任务以短延迟来复现类似的行为。在我的机器上会产生大量的 CPU 占用,并且 jstack
输出类似(有时更深入到 poll
方法中)。此代码可复现:
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, r -> new Thread(r, "scheduler"));
executor.setMaximumPoolSize(1);
executor.setKeepAliveTime(10_000, TimeUnit.MILLISECONDS);
executor.allowCoreThreadTimeOut(true);
AtomicInteger count = new AtomicInteger();
Runnable task = () -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(count.incrementAndGet() + " executed by " + Thread.currentThread());
};
executor.scheduleAtFixedRate(task, 0, 100, TimeUnit.MILLISECONDS);
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.setKeepAliveTime(1, TimeUnit.MINUTES);
executor.allowCoreThreadTimeOut(true);
for (long i = 0; i < 1000; i++) {
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
}
}, 0, 1, TimeUnit.NANOSECONDS);
}
现在我们只需要确定哪一段代码设置了有问题的 ScheduledThreadPoolExecutor
。我在 RabbitMQ 和 Netty 的源代码中查找,但没有发现明显的问题。这可能是您自己代码中的问题吗?
编辑:如评论中所述,根本原因是使用0
初始化的 ScheduledThreadPoolExecutor
在某些平台上可能会导致CPU旋转。这是在提问者的代码中完成的。
top -H -p <jvm pid>
和jstack <jvm pid>
,你就可以看到消耗 CPU 的线程以及这些线程的堆栈。我还建议运行jstat -gc <jvm pid>
来确保不是内存问题。 - K Erlandssontop
中进程的 pid 转换成十六进制来匹配线程。该十六进制数字与 jstack 输出中的 nid=0x<hexNumber> 匹配。这样我们就能够确定消耗 CPU 的确切线程。 - K Erlandsson