并且这种峰刺出现的频率不固定,查看 cat 发现,每小时出现的频率也固定,多的时候十几次,少的时候一两次。有告警信息可知,是 cat 采集到的 system.process:cpu.system.load.percent 指标超过 60% 导致。而这个指标一看就是系统 CPU 负载的指标。为了解决这个问题,首先要搞清楚,CPU 负载也就是 CPU Load 是个啥?,所以就有了这篇文章。
可能很多人都看到过一个线程数设置的理论:
CPU 密集型的程序 - 核心数 + 1
I/O 密集型的程序 - 核心数 * 2
这个理论看起来很合理,但实际操作起来,总是不仅如此人意。
下面,我们就来看一下 线程数, CPU 的关系先说一个基本的理论,大家应该都知道:
一个CPU核心,单位时间内只能执行一个线程的指令
那么理论上,我一个线程只需要不停的执行指令,就可以跑满一个核心的利用率。
下面我们用一段程序来测试下 CPU 和线程数 之间的关系。
CPU 利用率测试
测试环境:公司配置的 mac 电脑(配置太低,日常开发卡到怀疑人生)
2.3 GHz 双核Intel Core i5,(2 Core, 4 Threads)
8 GB 2133 MHz LPDDR3
来写个死循环空跑的例子验证一下:
public static void main(String[] args) {
testWithCalc();
}
public static void testWithCalc() {
while (true) {
}
}
运行这个例子之前,先来来看看现在CPU的利用率:
由 CPU Load 过高告警引发的对 线程数和 CPU 的思考
未运行之前
> 由于本身 有一些程序在跑可以看到,左上角 4 个 CPU 核心数(2核4线程,姑且认为 4个CPU核心),CPU 利用率都是个位数,看起来毫无压力。右上角 有个 Load average 表示 CPU 的负载,代表 CPU 的处理线程的繁忙程度。
接下来,运行上面的程序之后,再来看看 CPU 的利用率:
由 CPU Load 过高告警引发的对 线程数和 CPU 的思考
1 个线程
从图上可以看到,我的2号核心利用率达到 50% 多,但是为啥没有跑满呢,因为 CPU 执行线程是靠分配给线程时间片来运行不同的线程的,而我们的线程是个 while true 会一直循环 CPU ,所以 CPU 的利用率应该是 100 %没错,但是对于多核应该是多核的 CPU 利用率加起来,即
0号(28.9%)+1号(18.7)+2号(50.7%)+3号(7.9%) > 100%
那基于上面的理论,我多开几个线程试试呢?
public static void main(String[] args) {
testWithThreadCalc(2);
}
public static void testWithThreadCalc(int threadNum) {
System.out.println("start ...");
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
// 模拟计算操作
while (true) {
}
}).start();
}
}
我们先开两个线程,此时再看CPU利用率:
由 CPU Load 过高告警引发的对 线程数和 CPU 的思考
2 个线程
2 个线程运行我们的程序, 那么 整体 CPU 利用率 定会大于 200% ,即
0号(68.4%)+1号(35.8)+2号(68.0%)+3号(37.6%) > 200%
那如果开4个线程呢,是不是会把所有核心的利用率都跑满?答案一定是会的:
public static void main(String[] args) {
testWithThreadCalc(4);
}
public static void testWithThreadCalc(int threadNum) {
System.out.println("start ...");
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
// 模拟计算操作
while (true) {
}
}).start();
}
}
由 CPU Load 过高告警引发的对 线程数和 CPU 的思考
4 个线程
此时的结果不出我们所料,所有的 CPU 利用率依然达到 100%,而 右上角的 Load average 我们可以看到,才 10% 左右,说明, CPU 负载不是很高,如果这时再增加线程, CPU 调度就会开始繁忙起来,那么 CPU Load 也会增加,为了印证我们的猜想,把线程数调整到 100 试试(此时的我已带上头盔,因为怕这破电脑炸了。。。)。
public static void main(String[] args) {
testWithThreadCalc(100);
}
public static void testWithThreadCalc(int threadNum) {
System.out.println("start ...");
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
// 模拟计算操作
while (true) {
}
}).start();
}
}
由 CPU Load 过高告警引发的对 线程数和 CPU 的思考
100 个线程(此刻,电脑 CPU 风扇狂转)
随着风扇的嗡嗡声,可以看到,我的 4个CPU 还是 100% 的利用率,不过此时的 CPU 负载 Load average 也从 10% 升高到了 98.98%,说明此时CPU更繁忙,线程的任务无法及时执行。很明显,此刻,我的电脑已不堪重负。