EZLippi-浮生志

Linux性能问题定位总结

当一个系统出现前台响应慢、接口返回慢等情况时,可以考虑分析是否存在性能瓶颈了,性能瓶颈可以分为2种:

  1. CPU高(这里指长期使用率超过75%),包括用户态CPU+内核态CPU,这类问题比较常见,通过结合TOP命令和抓取线程堆栈可以找到耗资源最多的线程,然后分析调用堆栈进行优化

  2. CPU使用率不高,但是系统还是响应慢

这里主要介绍第二种场景,分析步骤如下:

排查Java进程是否在Full GC

对于Java进程响应慢,先要排查系统是否在频繁Full GC,Full GC时JVM暂停时间较长,所有线程会被挂起。一般来讲如果是这种场景,通过top -Hp Java进程ID 命令可以发现某个线程的CPU使用率一直处于99%左右(线程名称vm thread),从GC日志可以看到JVM一直在执行CMS GC(JVM老年代GC)。

首先在GC日志中搜索CMS-concurrent-mark-start关键字,查看相邻两次CMS GC的时间间隔,如下所示:

CMC GC基本上每分钟一次,GC频率太高,会导致JVM大部分时间都处理停顿状态, 每小时发生一次CMS GC就已经算频率高了,再来看GC停顿时间:

从GC日志来看, GC发生时新生代的eden区和from区基本上已经满了,正常场景要执行Minor GC了,但是可以看到老年代CMS区总大小6291456K,使用了6291455K,基本上用完了。JVM有一个线程每隔2秒钟去检查老年代的大小,如果超过指定的阈值(阈值可以通过jvm参数指定:-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=75),那就会触发一次CMS GC,从GC日志来看,在CMS GC期间又有新的在老年代创建对象的请求,此时又没有空间容纳新对象,就出现了concurrent mode failure这个错误,这个错误发生后JVM会放弃当前的CMS GC转而执行一次Full GC,Full GC会对新生代和老年代进行内存回收,大部分过程都是stop-the-world的,由GC日志可以看到整个JVM停顿了36.40秒。每隔一分钟执行一次Full GC,每次停顿36.4秒,可以想到JVM几乎一直不可用。GC回收之后新生代和老年代的内存使用率还是非常高,因此JVM一直在执行GC,如果JVM花费98%的时间都在GC并且回收的内存小于2%,那就会抛出Out-Of-Memort Error并退出jvm进程。

出现上图中的场景一般是有线程一直在创建对象,并且对象的创建速度高于回收速度,因此要分析这样子创建对象是否是合理的,如果是合理的,那要考虑调整JVM参数扩充内存。合不合理要根据业务场景来分析,一般是通过jmap命令获取当前jvm对象的直方图,找到哪些对象占了大部分的内存,并分析是否合理.

分析内存和磁盘

如果排除了JVM Full GC的可能性,那要分析是否是内存和磁盘的问题了:

一般都是首先通过top命令查看当前系统的负载,系统的负载代表的时在队列中等待执行的任务数量,对于单核CPU,负载为1表示已经达到了系统的负荷,对于多核CPU如果负载高于CPU核心数量也是达到了负荷,但是一般到达0.5 * CPU核心数时就要关注了。

比如下面的场景,load average 在1分钟、5分钟和15分钟平均值分别是5.92 5.58 5.48,CPU核心数为12. CPU使用率不高,正常场景25%的CPU使用率负载不会这么高,这个时候很可能是内存和磁盘有性能瓶颈。

需要注意的是,通过Top命令看到的只是表象,具体深层次的原因要通过vmstat和iostat两个命令来分析,分析过程如下:

使用vmstat 1 观察系统的负载情况,这个命令每隔1秒钟输出一次:

  • r列表示当前运行的任务数,如果超过cpu核心数那就会有任务要等待

  • b列表示当前正在阻塞等待的任务数,如果这个数量长期较大,表示执行的任务太多,cpu调度不过来

  • swapd列是分析内存的关键指标,swapd表示从内存交换的磁盘的空间大小,单位是Kb,当内存不时使操作系统会把一部分不使用的内存交换到交换空间(这个空间使用的是磁盘),正常场景这个值应该是0,当前是交换了6.7G内存左右,说明内存已经严重不足了;

  • si指标表示swap in,内存交换到磁盘的次数,当前交换次数较多.

  • so指标表示swap out,从磁盘交换回内存次数,从图中可以看出系统一直在swap in 和swap out,说明内存一直不够
    (这里需要注意的是,如果swapd小于100M并且swap in 和swap out一直为0,那系统也算正常)

  • free列表示空闲内存的数量。

再看一个正常环境的vmstat命令输出,swapd为79M, si和so一直为0,并且free内存充足。

使用iostat -x 命令观察io的使用情况:

每列指标的含义如下:

  • rrqm/s: 每秒进行 merge 的读操作数目
  • wrqm/s: 每秒进行 merge 的写操作数目
  • r/s: 每秒完成的读 I/O 设备次数
  • w/s: 每秒完成的写 I/O 设备次数
  • rsec/s: 每秒读扇区数
  • wsec/s: 每秒写扇区数
  • rkB/s: 每秒读K字节数,是 rsect/s 的一半,因为每扇区大小为512字节。(需要计算)
  • wkB/s: 每秒写K字节数,是 wsect/s 的一半。(需要计算)
  • avgrq-sz: 平均每次设备I/O操作的数据大小 (扇区)
  • avgqu-sz: 平均I/O队列长度
  • await: 平均每次设备I/O操作的等待时间 (毫秒)
  • svctm: 平均每次设备I/O操作的服务时间 (毫秒)(这个指标没有参考价值,可以忽略
  • %util: 一秒中有百分之多少的时间用于 I/O 操作,或者说一秒中有多少时间 I/O 队列是非空的。如果 %util 接近 100%,说明产生的I/O请求太多,I/O系统已经满负荷,该磁盘可能存在瓶颈。idle小于70% IO压力就较大了,一般读取速度有较多的wait.

从上面的图片来看,io不算忙碌,但是await高达86.67毫秒,也就是平均每个io请求要等待87毫米才处理完,从avgqu-sz来看队列中平均任务数才0.57,说明IO任务并不多,但是单个任务处理慢,一般就是磁盘处理速度慢。

🐶 您的支持将鼓励我继续创作 🐶