一、背景
日常的工作中,在衡量服务器的性能时,经常会涉及到几个系统指标,load、cpu、mem、qps、rt等。每个指标都有其独特的意义,很多时候在线上出现问题时,往往会伴随着某些指标的异常。大部分情况下,在问题发生之前,某些指标就会提前有异常显示。
对于这些指标的理解和查看、异常解决等,是程序员们重要的必备技能。本文,主要来介绍一下这几个系统指标一系统负载(Load)、CPU利用率、内存使用率。
二、系统指标异常分类
- CPU使用率分析和异常排查
- 系统负载(Load) 分析和异常排查
- 内存使用率分析和异常排查
- 网络I/0分析和异常排查
三、CPU使用率分析和异常排查
3.1 什么是CPU使用率
什么是CPU时间片? 我们现在所使用的Windows、Linux、Mac OS都是“多任务操作系统”,就是说他们可以“同时”运行多个程序,比如一边打开Chrome浏览器浏览网页还能一边聊钉钉。但是,实际上一个CPU内核在同一时刻只能干一件事,那操作系统是如何实现“多任务”的呢? 大概的方法是让多个进程轮流使用CPU一小段时间。由于这个“一小段时间”很短(在linux上为5ms-800ms之间),用户感觉不到,就好像是几个程序同时在运行了。上面提到的“一小段时间”就是我们所说的CPU时间片。CPU的现代分时多任务操作系统对CPU都是分时间片使用的。
CPU使用率,就是程序对CPU时间片的占用情况,即CPU使用率=CPU时间片被程序使用的时间/总时间。比如A进程占用10ms,然后B进程占用30ms,然后空闲60ms,再又是A进程占10ms,B进程占30ms,空闲60ms,如果在一段时间内都是如此,那么这段时间内的CPU占用率为40%。
大多数操作系统的CPU占用率分为用户态CPU使用率和系统态CPU使用率。用户态CPU使用率是指执行应用程序代码的时间占总CPU时间的百分比。相比而言,系统态CPU使用率是指应用执行操作系统调用的时间占总CPU时间的百分比。系统态的CPU使用率高意味着共享资源有竞争或者IO设备之间有大量的交互。
3.2 查看CPU使用率
3.2.1 top命令 (top参数详解)
cpu信息 | 含义 |
---|---|
1.2 us | 用户空间占用CPU百分比 |
0.7 sy | 内核空间占用CPU百分比 |
0.0 ni | 用户进程空间内改变过优先级的进程占用CPU百分比 |
97.5 id | 空闲CPU百分比 |
0.0 wa | 表示 CPU 在等待 IO 操作完成所花费的时间。系统不应该花费大量的时间来等待 IO 操作,否则就说明 IO 存在瓶颈。 |
0.4 hi | 表示系统处理硬中断所花费的时间 |
0.3 si | 表示系统处理软中断所花费的时间。 |
0.0 st | 物理机上其他虚拟机占用CPU的时间百分比 |
3.2.2 查看cpu信息
可以看到cpu厂商信息,核数,是否开启超线程技术等等,具体每个字段的含义可以参考:
https://www.cnblogs.com/wxxjianchi/p/10522049.html,写的十分详细了。
3.3 cpu突然飙升怎么办?
3.3.1 常见原因
- 代码存在死循环
- 内存问题,导致大量Full GC
- 宿主机超卖
3.3.2 原因排查
3.3.2.1 宿主机cpu超卖
top结果信息中心的cpu st的值,即宿主机cpu时间片分配给宿主机上其他虚拟机时间占比,如果st使用率较大,则cpu占用飙升是由于宿主机超卖导致。
3.3.2.2 内存问题,导致大量Full GC
通过arms监控进行查看Full GC和Young GC次数,如果发现的确存在频繁的Full GC,Dump应用的堆内存,对堆内存进行分析,定位异常代码。
3.3.2.3 代码存在死循环
需要先定位到占用大量cpu时间片的java线程,然后排查对应线程的代码定位原因,具体步骤如下:
Step1.使用top命令找到CPU占用最高的线程 (PID为进程ID),看一下是否为java线程
Step2.查看CPU占比靠前的iava线程,其中的PID为子进程ID,即iava线程ID
top-p 1972315 -H
Step3.获得iava线程的十六进制值,通过jstack定位线程堆栈,找到异常类
四、内存使用率异常
4.1 什么是内存
内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的。因此内存的性能对计算机的影响非常大。一旦内存使用出现异常,对程序会产生重大影响。内存也被称为内存储器,其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。
4.1.1 物理内存
物理内存指通过物理内存条而获得的内存空间,即随机存取存储器(random access memory,RAM),是与CPU直接交换数据的内部存储器,也叫主存 (内存)。
4.1.2 虚拟内存
虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上、在需要时进行数据交换(也就是说。当物理内存不足时,可能会借用磁盘空间来充当内存使用)。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。
4.2 查看内存使用情况
free命令
Mem是内存的使用情况。
buffers/cache 是物理内存的缓存统计情况,cache 是为了弥补高速设备和低速设备的鸿沟而引入的中间层,最终起到“加快访问速度”的作用,buffer的主要目的进行流量整理,把突发的大数量小规模的/0] 整理成平稳的下小数量较大规模的 IO,以减少响应次数(比如从网上下电影,你不能下一点点数据就写一下硬盘,而是积攒一定量的数据以后一整块一起写。
Swap是交换空间的使用情况。
4.3 内存飙升
4.3.1 常见原因
- 内存溢出
- 内存泄漏
- 堆外内存使用不当
4.3.2 原因排查
4.3.2.1 内存溢出
指程序运行过程中无法申请到足够的内存而导致的一种错误。内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况。
java heap space:堆内存溢出,jvm无法为新生成的对象分配内存,也即eden\survivot\old在gc后空间仍不够,可以检查一下是否有内存泄漏的现象,若没有,考虑调整jvm大小,优化代码的方式,减少创建对象的频率。
permgem space:方法区内存溢出,类信息、常量、静态变量创建太多,无法为其分配内存,一般是因为加载的类太多,或者反射、动态代理使用不当导致的。
4.3.2.2 内存泄漏
内存泄露是指程序中动态分配内存给一些临时对象,代码段运行结束后,这些对象已经没有被使用,但由于GCroots可达,没有被GC回收,始终占用内存。简单来说即被分配的对象无用但可达,这种问题一般是代码设计存在缺陷导致的。表现的现象一般是old区在full gc后平均水位高于内存泄漏之前。可以通过jmap工具查看哪些对象。排查过程可以参考这个案例,也比较详细了:https://segmentfault.com/a/1190000039842866
4.3.2.3 堆外内存使用不当
什么是堆外内存
和堆内内存相对应,堆外内存就是把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机),这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。
堆外内存的优点
- 减少了垃圾回收 因为垃圾回收会暂停其他的工作;
- 加快了复制的速度 堆内在fush到远程时,会先复制到直接内存,然后再发送,而堆外内存相当于省略掉了复制这个步骤;
堆外内存的缺点
堆外内存的缺点就是内存难以控制,使用了堆外内存就间接失去了JVM管理内存的可行性,改由自己来管理当发生内存溢出时排查起来非常困难。
堆外内存使用场景
NIO引入了一种基于通道与缓冲区的10方式,他可以使用Native数库直接分配堆外内存,然后通过一个存储在Java堆中的iavanio.DirectBvteBuffer对象作为这块堆外内存的引用进行操作。
DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与JAVA堆的最大值(-Xmx指定) 一样,一般用的比较少,了解一下即可。
五、系统负载(Load)分析和异常排查
5.1 什么是系统负载
系统负载(Load)是指在一段时间内CPU正在处理以及等待CPU处理的进程数之和的统计信息,也就是 CPU使用队列的长度的统计信息。
当CPU完全空闲的时候,平均负荷为0; 当CPU工作量饱和的时候,平均负荷为1
如果CPU每分钟最多处理100个进程,那么系统负荷0.2,意味着CPU在这1分钟里只处理20个进程,系统负荷1.0、意味着CPU在这1分钟里正好处理100个进程;系统负荷1.7,意味着除了CPU正在处理的100个进程以外,还有70个进程正排队等着CPU处理。
5.1.1多核或者单核多处理器
2个CPU,意味着电脑的处理能力翻了一倍,能够同时处理的进程数量也翻了一倍
所以,2个CPU表明系统负荷可以达到2.0,此时每个CPU都达到100%的工作量。推广开来,n个CPU的电脑,可接受的系统负荷最大为nx1.0
5.1.2 打个比方
把CPU想象成一座大桥,桥上只有一根车道 (一个CPU),所有车辆都必须从这根车道上单向通行。
系统负荷为0,意味着大桥上一辆车也没有。
系统负荷为0.5,意味着大桥一半的路段有车。
系统负荷为1.0,意味着大桥的所有路段都有车,也就是说大桥已经"满"了。但是必须注意的是,直到此时大桥还是能顺畅通行的。
系统负荷为1.7,意味着车辆太多了,大桥已经被占满了(100%),后面等着上桥的车辆为桥面车辆的70%。以此类推,系统负荷2.0,意味着等待上桥的车辆与桥面的车辆一样多;系统负荷3.0,意味着等待上桥的车辆是桥面车辆的2倍。总之,当系统负荷大于1,后面的车辆就必须等待了;系统负荷越大,过桥就必须等得越久。
CPU的系统负荷,基本上等同于上面的类比。大桥的通行能力,就是CPU的最大工作量;桥梁上的车辆,就是一个个等待CPU处理的进程(process)。
5.2 CPU使用率与负载(Load) 的区别
CPU利用率:显示的是程序在运行期间实时占用的CPU百分比
CPU负载:显示的是一段时间内正在使用和等待使用CPU的平均任务数
简单理解,一个是cpu的实时使用情况,一个是cpu的当前以及未来一段时间的使用情况
5.3 查看系统负载
5.3.1 uptime命令
load average: 0.01,0.03,0.00就是负载信息,分别表示1分钟、5分钟、15分钟内系统的平均负荷
5.3.2 w命令
5.3.3 top
不再赘述
5.4 机器正常负载范围
经验法则:对于单核机器来说
当系统负荷持续大于0.7,你必须开始调查了,问题出在哪里,防止情况恶化。
当系统负荷持续大于1.0,你必须动手寻找解决办法,把这个值降下来。
当系统负荷达到5.0,就表明你的系统有很严重的问题,长时间没有响应,或者接近死机了。你不应该让系统达到这个值。
5.5 一般会遇到哪些load飙升的场景
5.5.1 cpu使用率低而负载高(一般就是单任务执行时间太长,阻塞了)
主要原因是等待磁盘IO完成的进程过多,导致进程队列长度过大,但是cpu运行的进程却很少,这样就导致负载过大,但cpu使用率低,一般有以下场景:
- 磁盘读写请求过多导致大量IO等待
- MySQL中慢SQL
- 外接硬盘故障,如机器扩展了阿里云的NAS,但是NAS Server故障
磁盘读写请求过多就会导致大量I/O等待cpu的工作效率要高于磁盘,而进程在cpu上面运行需要访问磁盘文件,这个时候cpu会向内核发起调用文件的请求,让内核去磁盘取文件,这个时候会切换到其他进程或者空闲,这个任务就会转换为不可中断睡眠状态。当这种读写请求过多就会导致不可中断睡眠状态的进程过多,从而导致负载高,cpu低的情况。
5.5.2 其他情况
除了上面的典型场景,还存在以下几种场景,如内存高load高,cpu高load高,内存高cpu高load高,遇到这种复杂情况,首先排除是不是发布导致的,然后通过慢sql,并发量是否飙升,内存有无泄漏,是否出现大型cpu计算任务等维度,同时结合你的应用本身是cpu密集型还是io密集型等因素一一分析。