type
status
date
slug
summary
tags
category
icon
password
CPU 飙升这种问题在线上环境非常常见,也是面试常问提,一定要掌握高效的排查问题的手段。排查思路:既然是 CPU 飙升,肯定是查一下耗 CPU 的线程,然后看看 GC。
1、使用原生命令排查
1、找出占用 CPU 最高的 java 进程的 pid。
查看所有进程占系统 CPU 的排序,极大可能排第一个的就是咱们的 java 进程(COMMAND列)。PID 那一列就是进程号。
2、找出 java 进程下占用 CPU 最高的线程 tid。
查看 java 进程下的所有线程占 CPU 的情况,一般 COMMAND 列第一个就是占用 CPU 最高的线程 tid。
如果使用Htop
命令,会同时显示进程 pid 和线程 tid。按使用 CPU 排序,第一个 COMMAND 列为白色字体的就是 java 进程,第一个 COMMAND 列为黄色字体的就是 java 进程下的线程。
3、线程 tid 转十六进制。
后续查看线程堆栈信息展示的都是十六进制,为了找到线程堆栈信息,需要把线程号转成十六进制。例如
printf "%x\n" 10
打印:a
,那么在 jstack 中线程号就是 0xa
。4、查找线程 tid 的线程堆栈信息。
5、查看堆内存和 GC 情况。
查看一下进程的堆内从是不是要溢出了,特别是老年代内从使用情况一般是达到阈值(具体看垃圾回收器和启动时配置的阈值)就会进程Full GC。
查看某进程GC持续变化情况,如果发现返回中 FGC 很大且一直增大,确认Full GC
6、生成 dump 文件。
把 heap-dump.hprof 下载到本地,通过 mat、jprofiler 等工具分析内存。
7、如果确认 java 程序没有问题,排查服务器本身运行情况。
2、使用Arthas排查
1、安装并启动 Arthas
在生产环境中,我们一般是选择其中一台机器开启Arthas监控,因为使用 Arthas 对系统性能会有一定影响。
Arthas 会显示当前运行的 Java 进程列表,选择要诊断的 Java 进程,然后就会连接到目标进程。
2、用 dashboard 命令查看系统概况
连接到目标进程后,使用 dashboard 命令查看系统的整体性能概况,包括 CPU 使用情况
3、使用 thread 命令定位高 CPU 线程
使用 thread 命令查看所有线程的信息,并找出 CPU 使用率高的线程
4、查看具体线程的堆栈信息
3、紧急处理
- 先确定确认是不是程序问题,如果是程序问题,例如前一天或者最近有新版本发布或者依赖域的服务有新版本发布,第一时间回滚镜像。
- 如果不是程序问题,就先申请紧急扩容,增加机器。
4、原因分析
4.1 Full GC 频繁
问题原因:这种问题最常见,一般有两个原因:
- 存在内存泄漏问题,导致老年代可用内存减少,GC 线程只能疯狂执行来回收内存。
- 没有内存泄漏问题,纯粹是因为并发量大,或者做大批量数据处理,导致大量对象堆积在老年代内存,GC 也被迫不停执行来回收内存。
这种问题一般表现特征是:
- 系统卡顿,一个请求卡半天没响应。
- 第 4 步通过 jstack 命令可以看到占用 CPU 最高的线程主要是 GC 线程。
- 第 5 步通过 jstat 命令监控 GC 情况,可以看到 Full GC 次数非常多,并且次数在不断增加。
解决方案:
- 分析 dump 内存文件,找出内存泄漏的原因。
- 如果不存在内存泄漏问题,机器扩容或者升级配置。
4.2 频繁 Young GC
问题原因:频繁的垃圾回收(Young GC)操作会占用大量CPU资源,导致性能下降。例如程序中频繁创建和销毁对象,导致 GC 频繁触发。
解决方案:优化对象创建和销毁,减少临时对象的生成。
4.3 代码存在大量消耗CPU的操作
问题原因:代码中可能存在某些复杂算法、打印日志、无限循环递归等问题。
解决方案:第 4 步通过 jstack 命令可直接定位到问题的代码行,看一下是不是代码逻辑问题。
4.4 代码存在死锁问题
问题原因:发生死锁后,一般会存在忙等待或自旋锁等编程问题,从而导致繁忙等待问题,导致 CPU 飙升。例如不适当地使用 synchronized 块。死锁不会直接导致 CPU 资源占用过高,synchronize 和 AQS 中锁的设计是线程获取锁失败时,会主动挂起线程,而不会自旋循环检测锁是否被释放。 如果因为死锁,阻塞线程越来越多,内存占用也越来越高且无法释放,导致不停的 GC,会造成 CPU 占用飙升。
解决方案:第 4 步通过 jstack 命令,会明确提示 deadlock 问题,并打印出现 deadlock 的代码块。
4.5 线程数过多,导致频繁的上下文切换
问题原因:系统中存在大量并发线程,线程切换频繁,导致 CPU 资源被大量消耗在上下文切换上。例如:
- Web 服务器同时处理大量请求,每个请求都创建一个新线程
- 一个程序中开启了数百个线程,每个线程都在不断进行 I/O 操作。
解决方案:使用线程池来限制并发线程数量
4.6 系统资源不足,例如大量使用虚拟内存
问题原因:系统内存不足时,就会将磁盘存储作为虚拟内存使用,而虚拟内存的运行速度要慢得多。这种过度的分页和交换会导致 CPU 占用率居高不下,因为处理器需要花费更多时间来管理内存访问,而不是高效地执行进程。例如:直接一次性加载一个非常大的文件到内存中,导致内存不足。
这种问题的特征:观察 top 和 vmstat 命令返回的性能指标。这两个命令返回的指标其实有部分重叠。top 重点关注 CPU 和系统负载相关的指标,vmstat 重点关注内存相关的指标。
关注几个重点指标:
- 第一行,CPU 负载(Load),是对某一时间段内 CPU 正在处理和等待处理的进程数之和的统计信息。load average 后面的三个值,分别表示1分钟、5分钟、15分钟的负载情况。这个值越低越好,如果平均负载大于 CPU 数量,甚至达到 3 ~ 5倍 CPU 数量,说明系统负载非常大了。
- 第三行,CPU 使用情况。 这行数据会优先看 id 数据,如果 id 很大,不用分析;如果 id 小,再去看 us 和 sy。
- us:CPU 的用户态使用时间占比。如果这个高,可能代码逻辑复杂,一般需要分析代码。
- sy:CPU 的系统态使用时间占比 。如果这个高,要看看 ni 的值,ni 很高的话,就要进一步分析 hi si 的值,看哪个中断高。
- id:idle 空闲,不会超过100。
- wa:IO wait 等待 IO 操作的操作状态的时间占比 。当我们 IO 繁忙时,这个数值一般会非常明显。
- ni:中断占用 CPU 的情况,如果发生中断,优先级更高的中断了当前的事情。一般来说 ni = hi + si。
- hi:hard interrupt 硬中断占用 CPU 的时间。
- si:soft interrupt 软中断占用 CPU 的时间。
关注几个重点指标:
- 进程相关:
- r:表示系统中 CPU 等待处理的线程。一个 CPU 每次只能处理一个线程,所以该数值越大,通常表示系统运行越慢。
- 内存相关:
- si :每秒从磁盘读入虚拟内存的大小,如果这个值大于0,表示物理内存不够用或者内存泄露了,要查找耗内存进程解决掉。我的机器内存充裕,一切正常。
- so:每秒虚拟内存写入磁盘的大小,如果这个值大于0,同上。
- swpd:表示虚拟内存(swap)已使用的大小,一般情况下是 0。如果大于 0,通常还需要结合 si 和 so 这两个数据指标来一起分析,如果 si 和 so 还维持在 0 左右,那服务器的物理内存还是够用的。
- CPU 调度相关:
- cs:每秒上下文切换次数,例如我们调用系统函数,就要进行上下文切换,线程的切换,也要进程上下文切换,这个值要越小越好,太大了,要考虑调低线程或者进程的数目。
- us:用户模式消耗的 CPU 时间百分比。该值较高时,说明用户进程消耗的 CPU 时间比较多。如果该值长期超过 50%,则需要对程序算法或代码等进行优化。
- sy:内核模式消耗的 CPU 时间百分比。该值较高,表示系统调用时间长,例如是 IO 操作频繁。
- wa:I/O 等待消耗的 CPU 时间百分比。该值较高时,说明 IO 等待比较严重,这可能是磁盘大量作随机访问造成的,也可能是磁盘性能出现了瓶颈。
- id:处于空闲状态的 CPU 时间百分比。如果该值持续为 0,同时 sy 是 us 的两倍,则通常说明系统面临 CPU 资源短缺。
解决方案:服务器扩容或者配置升级。
5、常用工具
5.1 命令行终端
- 标准终端类:jps、jinfo、jstat、jstack、jmap
- 功能整合类:jcmd、vjtools、arthas、greys
5.2 可视化界面
- 简易:JConsole、JVisualvm、HA、GCHisto、GCViewer
- 进阶:MAT、JProfiler
命令行推荐 arthas ,可视化界面推荐 JProfiler,MAT 教程参考
5.3 在线工具
- jstack:一款在线分析线程堆栈的工具,可以上传
jstack
得到的线程堆栈文件。
- fastthread:同上,可以和 jstack 混合使用。
- heaphero:一款在线的简单易用的内存分析工具,无需登录在线生成分析报告,可以上传
jmap
得到的内存快照文件。
- gceasy:一款在线的 GC 日志分析器,可以通过 GC 日志分析进行内存泄露检测、GC 暂停原因分析、JVM 配置建议优化等功能。使用前需要添加 JVM 参数
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/tmp/gc.log
,然后就可以上传gc.log
文件。
- Author:mcbilla
- URL:http://mcbilla.com/article/10a85c7d-7c1d-80f6-abf3-ecec70612272
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts