CPU 利用率,又称 CPU 使用率。顾名思义,CPU 利用率用于描述 CPU 的运行情况,反映了一段时间内 CPU 被程序占用的情况。使用率越高,表示计算机在该时间段内运行了更多的程序,反之则较少。CPU 的利用率与其性能直接相关。
现代操作系统如 Windows、Linux 和 MacOS 都是多用户、多任务的分时操作系统。这意味着多个用户可以在同一时间“同时”进行多项操作,这已经成为我们日常生活的一部分,显得非常普遍。然而,在单个 CPU 计算机中,实际上同一时间只能处理一项任务。
为了实现看似“同时处理多项任务”的效果,分时操作系统将 CPU 时间划分为长度基本相同的时间片段,也就是“时间片”。操作系统通过管理这些时间片,依次分配给各个用户使用。
如果某个作业在其分配的时间片结束前没有完成,该作业会被暂停,释放 CPU,等待下一个时间片再继续执行。此时 CPU 会被分配给另一个作业使用。由于计算机处理速度非常快,适当设置时间片的长度使得用户在时间片间隙感知不到停顿,仿佛整个系统是在独占 CPU 一样。
因此,我们提到的 CPU 占用率通常指的就是 CPU 在时间片内被占用的情况。
查看 CPU 利用率
在 Linux 系统中,确实可以使用一些命令来查看系统的负载情况和 CPU 利用率。这些命令包括:
- uptime: 显示系统的运行时间以及平均负载。
- top: 实时显示系统中各个进程的资源占用情况,包括 CPU 利用率、内存占用等。
- w: 显示当前登录用户和各用户的活动信息,包括登录时间、运行的命令等。
- vmstat: 报告系统的虚拟内存统计信息,包括 CPU 利用率、内存利用率、磁盘 I/O 等。
这些命令在终端中执行后,可以帮助管理员实时监控系统的性能和资源使用情况,从而及时进行调整和优化。
vmstat 命令
vmstat 命令是 Linux/Unix 系统中常见的监控工具,能够展示在指定时间间隔内服务器的各种状态值,包括 CPU 利用率、内存使用情况、虚拟内存交换情况以及 IO 读写情况。
~ vmstat
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 1 0 2446260 0 3202312 0 0 201 16304 1 6 0 0 84 5 1
从上述结果中,我们可以获取大量信息,但本文重点关注 CPU 部分的指标。
us sy id wa st
0 0 84 5 1
以上几个指标是当前 CPU 的占用情况。
- %us: 用户进程执行时间百分比。
- %sy: 内核系统进程执行时间百分比。
- %id: 空闲时间百分比。
- %wa: IO 等待时间百分比。
- %st: 虚拟 CPU 等待实际 CPU 的时间百分比。
当%us 较高时,表示用户进程占用了大量 CPU 时间。然而,如果长期超过 50%,则需要考虑优化程序算法或加速处理。
高%sy 表明系统内核消耗了大量 CPU 资源,这不利于系统正常运行,应当寻找问题根源。
%wa 的高值表示 IO 等待较为严重,可能是由于磁盘随机访问频繁或磁盘性能瓶颈引起的块操作问题。
通常使用 vmstat 工具时,需要指定两个数值参数。第一个参数表示采样的时间间隔,单位为秒;第二个参数表示采样的次数。
~ vmstat 2 2
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 2479444 0 3165172 0 0 196 15905 2 8 0 0 84 5 11
0 0 0 2479404 0 3165176
以上命令表示采集两次数据,每隔 2 秒采集一次。
top 命令
top 命令是 Linux 系统下常用的性能分析工具,能够实时显示系统中各个进程的资源占用情况,类似于 Windows 中的任务管理器。
~ top
top - 10:58:07 up 18:13, 1 user, load average: 0.32, 0.24, 0.19
Tasks: 64 total, 1 running, 63 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.1%us, 0.2%sy, 0.0%ni, 92.8%id, 0.1%wa, 0.0%hi, 0.0%si, 6.8%st
Mem: 8388608k total, 5928076k used, 2460532k free, 0k buffers
Swap: 16777216k total, 0k used, 16777216k free, 3181996k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2393 admin 20 0 5056m 2.2g 56m S 4.3 27.6 79:06.21 java
1054 root 20 0 338m 9760 5112 S 0.3 0.1 2:37.30 logagent
从上述打印信息中,我们可以看到第三行反映了当前 CPU 的整体情况。
此外,我们还能观察到 ID 为 2393 的 Java 进程当前内存使用率最高,约占 4.3%。
由于 Java 是多线程的,有时候我们希望能够查看一个 Java 进程中所有线程的 CPU 使用情况,这也可以通过 top 命令来实现。
~ top -Hp 1893
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
19163 admin 20 0 5056m 2.2g 56m S 1.7 27.6 17:39.97 java
10649 admin 20 0 5056m 2.2g 56m S 0.7 27.6 4:07.64 java
5884 admin 20 0 5056m 2.2g 56m S 0.3 27.6 2:18.19 java
10650 admin 20 0 5056m 2.2g 56m S 0.3 27.6 1:24.77 java
通过执行top -Hp 1893命令,我们可以发现,当前进程 ID 为 1893 的 Java 进程中,线程 ID 为 19163 的线程占用 CPU 最高,大约达到 1.7%。
PS:top 命令的输出结果是动态变化的,会随着系统情况的变化实时更新。
CPU 使用率的计算逻辑
描述系统 CPU 使用情况主要包括以下几个方面:
- user: 自系统启动以来,CPU 处于用户态运行的时间,不包括 nice 值为负的进程。
- nice: 自系统启动以来,CPU 中 nice 值为负的进程占用的时间。
- system: 自系统启动以来,CPU 处于内核态运行的时间。
- idle: 自系统启动以来,CPU 除了 iowait 以外的空闲等待时间。
- iowait: 自系统启动以来,CPU 等待 IO 操作完成的时间。
- irq: 自系统启动以来,CPU 处理硬中断花费的时间。
- softirq: 自系统启动以来,CPU 处理软中断花费的时间。
- steal: 自系统启动以来,CPU 被其他虚拟环境中的操作系统“偷走”的时间。
- guest: 自系统启动以来,CPU 运行在通过 Linux 内核控制的客户操作系统上的虚拟 CPU 的时间。
- guest_nice: 自系统启动以来,CPU 运行在通过 Linux 内核控制的客户操作系统上的 nice 值为负的虚拟 CPU 的时间。
理解了以上参数的含义,计算某段时间内的 CPU 使用率并不复杂。假设我们有两个时间点,t1 和 t2,可以通过以下公式来计算 CPU 在这段时间内的总使用时间:
( user2+ nice2+ system2+ idle2+ iowait2+ irq2+ softirq2 + steal2 + guest2 + guest_nice2 ) - ( user1+ nice1+ system1+ idle1+ iowait1+ irq1+ softirq1 + steal1 + guest1 + guest_nice1)
CPU 的空闲时间:
(idle2 -idle1)
CPU 在 t1 和 t2 时间内的使用率:
CPU非空闲时间/CPU总时间*100%=(1-CPU的空闲时间/CPU总时间)*100%
则:
CPU(t1,t2)使用率:1-(idle2-idle1)/(( user2+ nice2+ system2+ idle2+ iowait2+ irq2+ softirq2 + steal2 + guest2 + guest_nice2 ) - ( user1+ nice1+ system1+ idle1+ iowait1+ irq1+ softirq1 + steal1 + guest1 + guest_nice1))
CPU 利用率和负载
很多朋友常常分不清楚 CPU 利用率和负载之间的区别与联系。
CPU 利用率是对某一时间段内 CPU 使用情况的统计,通过这个指标可以了解 CPU 在特定时间段内被使用的情况。
而 CPU 负载(Load)则是对某一时间段内 CPU 正在处理和等待处理的进程数之和的统计信息,也可以理解为 CPU 使用队列的长度统计。
可以用一个比喻来解释,将 CPU 的使用比作排队打电话:
我们可以将 CPU 比喻为一个电话亭,每一个进程就像是需要打电话的人。假设有一个单核计算机,现在有 10 个人需要使用电话(代表 10 个进程)。电话使用规则是管理员按顺序给每个人分配 1 分钟的通话时间。如果一个人在 1 分钟内完成通话,他可以将电话交还给管理员。但如果在 1 分钟内没有完成通话,他需要重新排队等待再次分配。在电话亭使用过程中,会有人打完电话离开,有人没打完电话选择重新排队,也会有新人来排队,这种人数的变化就相当于任务数的增减。
CPU 负载统计了一段时间内所有正在使用电话的人以及等待分配电话的人数的平均值。为了得到平均负载情况,我们每 5 分钟统计一次人数,并在第 1、5、15 分钟时取平均值,从而得到 1、5、15 分钟的平均负载。
而 CPU 利用率则统计了进程实际使用电话的时间与在电话亭内停留的总时间的比率。例如,一个用户获得了 1 分钟的使用权,在 10 秒内打了电话,接着花了 20 秒查电话簿,剩下的 30 秒又打了一个电话。那么他的利用率就是(10+30)/60。
Java Web 应用 CPU 使用率飙高排查思路
当发现系统的 CPU 使用率突然升高,首先需要确定是哪个进程造成了 CPU 负载的增加。在 Java 代码中,导致 CPU 占用高的原因可能包括以下几点:
- 内存泄漏导致大量 Full GC:例如典型的 Java 1.7 之前的 String.subString 方法可能会导致内存泄漏问题,进而引发频繁的 Full GC 操作。
- 代码中存在死循环:特别是在多线程场景下,使用不当的数据结构如 HashMap 可能导致死循环,使得某些线程消耗大量 CPU 资源。
解决这些问题的基本步骤是首先定位占用 CPU 较多的进程和线程,然后通过相应的命令查看这些线程的执行情况,并分析代码以定位问题。关键在于熟练使用 jstack、jstat 以及 jmap 等工具来定位和解决 Java 进程中的问题。
那么如何在真实环境中排查 CPU 飙高的问题呢?由于本篇幅过长,感兴趣的小伙伴可以关注,下期出。