
平时帮人排查性能问题的时候最常听到的一句话就是“明明压力都到顶了CPU就是上不去是不是机器坏了”我用“cpu占不上去”当关键词搜了一圈发现这个话题下面的讨论五花八门有人说是程序写得不行有人说是虚拟化限制还有人直接怀疑云厂商偷工减料。作为一个常年跟CPU使用率、系统负载和压测指标打交道的工程师我觉着很有必要把这类问题从头到尾捋一遍。CPU占不上去本质上是“程序的计算需求没有得到CPU时间的满足”。这句话听起来像废话但真往深里挖你会发现它背后藏着至少七八种完全不同的原因每种原因的排查路径和解决方案都不一样。这篇文章不会只讲某个编程语言或者某个具体框架而是把通用的排查思路、工具使用和定位方法系统性地过一遍后端开发、运维、SRE甚至做数据分析偶尔跑脚本的人都能从中找到自己需要的东西。1. 问题全貌先搞懂“占不上去”到底意味着什么很多人在说“CPU占不上去”的时候其实没搞清楚自己到底在抱怨什么。是CPU使用率只有30%还是某个核已经打满了但其他核闲着还是压测QPS上不去但CPU确实不高这三个问题的答案指向的方向完全不同。1.1 表象千奇百怪本质只有一句话CPU使用率这个东西本意是衡量CPU处于忙状态的时间比例。一台8核机器某个进程跑到满负荷总CPU使用率就是12.5%如果把8个核全跑满才是100%。很多人习惯性地把“CPU使用率100%”当成性能最优的标志这本身就是一个误解。更合理的指标是“任务吞吐量”——单位时间内完成了多少有效工作。我见过好几种典型的“占不上去”现象第一种压测时QPS到5000就上不去了CPU只有35%再增加并发也没用。 第二种top里看某个进程CPU显示100%但机器是32核总CPU使用率连5%都不到。 第三种CPU使用率倒是挺高但大部分时间花在sys内核态上us用户态低得可怜。 第四种整台机器CPU、内存、磁盘都没有任何瓶颈但程序就是慢得离谱CPU在“等”什么东西。这些表象背后的本质其实只有一个程序没有在“算”而是在“等”。等锁、等IO、等网络、等GC、等调度。或者程序压根就没有足够的并行度去消费CPU资源。1.2 为什么会“占不上去”先把两类原因分清我习惯把“占不上去”的原因分成两大类一类是“任务不够”一类是“任务被堵住”。任务不够指的是程序本身能并行的任务数量就很少。比如你写了个单线程脚本去处理文件不管机器有多少核它都只能用一个核。再比如你对一个单表查询做压力测试数据库连接池只有5个连接那再大的压力也只会排队CPU当然上不去。任务被堵住则是任务数量其实是够的但每个任务都在等待某个资源才能继续执行。常见的包括锁竞争多个线程抢同一个锁、IO等待磁盘慢或网络慢、线程池队列耗尽线程都在等上游返回、语言运行时限制GIL锁、GC停顿等。还有一种特殊情况是“程序压根没在干活”。比如某个客户端配置了很长的超时时间上游服务一直不返回线程全部挂起CPU看起来就是“很闲”的状态。热词里有一条“gpu cpu 内存占用都不高但卡”这种情况通常也不是真的没资源而是某个隐蔽的等待点把链路卡死了所有资源都在闲着等人来用。2. 动手排查前的思路准备拿到一台“CPU占不上去”的机器别急着执行命令。先花两分钟想清楚几个问题能省下后面一大堆弯路。2.1 先问自己三个问题第一个问题你的程序是单线程还是多线程目标并发是多少这是最基本的一个判断维度。单线程程序再怎么优化CPU也吃不满多核。第二个问题你希望CPU跑满还是希望任务在合理时间内完成有些场景CPU不跑满反而是健康的比如IO密集型的数据库服务它的瓶颈在磁盘和缓存CPU占用本来就不该高。第三个问题瓶颈可能在计算、存储还是网络你可以看程序是本地跑批处理还是通过网络读写远端服务这是决定排查方向的关键。把这三个问题的答案写在纸上再开始看数据。很多时候CPU占不上是“预期目标定错了”而不是“系统出了问题”。2.2 排查工具箱先备好这些基础命令工欲善其事必先利其器。CPU问题的排查需要一套完整的工具箱按系统类别分大概是这样Linux下必备的是top、htop、pidstat、mpstat、vmstat、sar、perf、strace。top看整体状态pidstat按进程和线程看CPUmpstat看每个核的使用情况vmstat看IO和上下文切换sar看历史数据perf做热点分析strace跟踪系统调用。Java应用还需要jstack看线程堆栈、jstat看GC情况、jcmd做诊断。Python应用可以用py-spy dump线程栈尤其是遇到GIL问题的时候特别好用。容器场景下docker stats看容器资源消耗cgroup的cpu.max和cpu.stat文件看配额限制。Windows下面用任务管理器、资源监视器、Process Explorer就够用wmic命令在部分旧系统里还能用新系统里就要用PowerShell替代了。这些工具不需要全部精通但你至少要清楚每一个能看什么。我在排查时最常用的是top pidstat jstack这三个已经能解决80%的问题。2.3 从热词里看到的对照案例先给“占不上”和“占用高”对个标搜索引擎里“cpu占不上去”和“占用高”几乎是两个热度相当的话题。很多人觉得它们是完全对立的方向实际排查时我发现它们经常是一根藤上的两个瓜。比如“服务主机dcom占用cpu高怎么解决”和“comsurrogate占用cpu过高”这类Windows系统上的异常占用问题本质上和“占不上去”是同一个排查坐标系先找进程归属再看线程状态最后看外部依赖。反过来“cpu压力测试怎么开”这种问题也很常见——想做压测但不知道用什么工具。这其实反映了另一个痛点很多人对CPU工作状态的判断依据不足既不知道怎么制造负载也不知道怎么看负载是否吃到了预期的CPU份额。后面我会专门讲压力测试和判断标准。3. 由浅入深八种“占不上去”的典型场景与定位方法这一节是全文的核心。我把实战中遇到的“CPU占不上去”场景归纳成了八种每种都有典型现象、底层原因、定位方法和解决方向。3.1 场景一单线程程序任务没法并行这是最常见的“新手向”问题。你写了个Python脚本处理日志或者用Node.js写了个CPU密集型的计算服务天生就是单线程/单进程模型。跑起来之后top里看到的现象是某一个核100%打满其他核全部接近0%进程总CPU占比只有1/NN是核数。定位方法很简单top看进程CPU是不是已经接近100%单核再看进程的线程数。如果线程数很少或者虽然线程多但只有一个在工作基本就是单线程的问题。Java里可以用jstack看线程状态Python里直接用py-spy dump看当前的调用栈Node.js可以用--prof做性能分析。解法也很直接能并行拆分的任务就拆分——多进程、多线程、多实例不能拆分的就接受现实。另外Python还有个特别的GIL锁问题CPU密集型任务用多线程是跑不满多核的必须用multiprocessing或者改成异步IO模型。热词里提到的“cpu智能核心调度”在这种场景下帮不上什么忙因为调度只能针对可并行的线程单线程程序本身就没有并行度可调度。3.2 场景二锁竞争导致线程都在“等”如果说场景一是“没得并行”场景二就是“看着能并行其实都堵在一起排队”。多线程程序里常见的坑锁的粒度过大、热点资源比如一个共享的HashMap被大量并发访问、数据库连接池被线程争抢等等。这种问题的现象很有迷惑性进程的CPU占用不高比如20%但线程数很多而且压测时无论怎么加压力QPS就是上不去。用jstack抓线程栈会看到大量线程处于BLOCKED或WAITING状态大部分线程都在等待同一个锁对象。定位这类问题的两个核心工具是jstack和perf。jstack PID抓线程栈grep出BLOCKED和WAITING的线程看它们等待的是哪个锁perf top能看到内核热点的开销如果是自旋锁导致的CPU的sy部分会明显偏高。解法集中在几个方向缩小锁的粒度synchronized大方法改成只锁关键代码块、读写锁分离、用ConcurrentHashMap等并发容器替代普通容器、无锁化设计CAS、原子类、减少共享可变状态。我在实际项目里遇到过最夸张的一次是一个统计接口里用了全局锁压测时CPU只有12%把锁改成分段锁之后直接跑满4核QPS翻了6倍。3.3 场景三IO和网络阻塞CPU在干等CPU占不上去的另一个大方向是“数据到不了”。磁盘IO慢、网络延迟高、远程服务响应慢都会让CPU在计算之前先陷入长时间的等待。这个场景在压测和线上故障里出现的频率非常高。判断方法靠vmstat和top的配合。top第一行里如果waIO等待占比很高说明CPU在等磁盘如果us不高但网络连接数异常高则多半是网络链路问题。vmstat里看bi/bo块设备读写和cs上下文切换可以辅助判断IO等待的严重程度。举一个我踩过的典型坑一个数据处理任务逻辑很简单每条记录做一次正则匹配加一次数据库写入。起初CPU死活上不去只有15%左右。后来用strace跟踪发现程序频繁调用fsync等待磁盘落地一次等待几十毫秒CPU当然没法保持忙碌。解法是批量写入、异步刷盘、加缓存调整之后CPU直接跑到了80%以上。这一类的排查难点在于“看起来哪都没问题但就是快不起来”。记住一个原则CPU不忙的时候一定有个东西在忙只是你没去看它。看看磁盘队列长度、看看网络重传率、看看下游服务的响应时间总能找到那个“在忙”的环节。3.4 场景四线程池配置不合理任务在排队线程池这个东西配得好是加速器配不好就是瓶颈制造机。线程数太少消费能力不足任务全在队列里堆积CPU吃不饱线程数太多上下文切换开销剧增有效计算时间被大量浪费。线程数太少的现象CPU不高但压测端已经大量超时中间件监控里线程池队列深度持续上涨。这时候哪怕压力再翻倍CPU也不会有明显变化因为消费者就那么几个。线程数太多的现象CPU使用率反而很高但大部分时间花在sys上us不高vmstat的cs上下文切换数值高得吓人压测的QPS却很低。线程池大小怎么定没有万能公式但有两个经验起点CPU密集型任务线程数设为N1N是CPU核数IO密集型任务线程数可以到2N甚至更高具体要看阻塞比例。关键是实测调整不要迷信公式。另外如果用的是Java的ThreadPoolExecutor一定要给线程池设置合理的有界队列和拒绝策略不然任务堆积导致内存溢出那就不是CPU的问题了。3.5 场景五语言运行时和GC的限制这一条主要针对Java和Python这类有运行时环境的语言。Java的GC机制、Python的GIL、Node.js的事件循环都会在特定条件下让CPU“占不上去”或者“占比虚高”。Java应用最常见的问题是GC竞争堆太大或太小、GC算法选择不当、对象分配速率过高导致GC线程疯狂工作但应用线程全在等待STWStop The World。现象是CPU占用不低很多花在GC线程上但业务吞吐上不去。定位用jstat -gcutil PID每隔几秒看一眼GC曲线如果Young GC非常频繁或者Full GC持续很久就要去查堆配置和对象分配行为了。Python的问题就是GIL。多线程程序在CPU密集型场景下GIL会导致同一时刻只有一个线程在解释器里执行字节码其他线程都在等待锁。表面上看线程很多实际吃不满多核。解决方向是改用多进程模型、用C扩展分担计算、或用异步IO把CPU密集和IO密集分离。热词里的“pytorch安装教程cpu”和“mineru cpu api”这一类场景本质上也涉及底层计算库的线程调度问题——如果只有CPU版本而没有GPU程序在计算层可能只能用有限的核心。还有一种值得一提的情况某些基础库对指令集有硬性要求比如热词里的“cellranger error: this cpu does not support avx”。AVX是CPU的向量指令集扩展很多生物信息学、AI推理的库编译时默认开启AVX支持老CPU不支持就会直接报错程序压根跑不起来更谈不上吃CPU。遇到这种就先确认CPU型号和指令集支持情况lscpu里看flags有没有avx2没有的话就换硬件或者找低版本兼容库。3.6 场景六CPU亲和性和容器CPU限额这个场景在容器化和云原生环境里特别常见。进程明明在跑任务量也很足但CPU就是卡在某个数值上不去往往不是程序的问题而是运行环境把它的CPU“关”起来了。CPU亲和性taskset会把进程绑定到指定的核心上。比如一台16核机器进程被绑到0号和1号核上那它最多只能用到2个核相当于12.5%的总CPU。检查方式是taskset -pc PID看affinity list是不是完整范围。容器CPU限额更隐蔽。docker run时设置了--cpus2或者k8s里设置了limits.cpu: 2进程就只能用2个核的CPU时间即使宿主机还有大量空闲核。在容器内用top看CPU使用率你会看到系统内存显示的总核数是宿主机的核数但cgroup会无情地把你的CPU时间限制住。检查方式是cat /sys/fs/cgroup/cpu.maxcgroup v2或者/sys/fs/cgroup/cpu/cpu.cfs_quota_uscgroup v1看看配额是多少。有意思的是很多人在这种场景下会误判成“程序性能不好”。我接手过一个项目容器里配了requests: 1, limits: 2压测时CPU始终在100%上下容器视角但业务量就是上不去。后来才发现这个100%是“配额内的100%”不是“宿主机的100%”。把limits调到4核之后问题立刻消失。热词里提到“24核cpu怎么看”放到容器场景里也要先分辨这个24核是宿主机核数还是容器配额核数。3.7 场景七指令集与架构不匹配导致的计算能力“扭曲”这类问题表面上是“CPU占不上去”实际上却是“软件层面就没打算让CPU干活”。比如热词里的“cellranger error: this cpu does not support avx”这类报错常见于某些生物信息学软件或机器学习框架。它们编译时默认开启了AVX/AVX2指令集老CPU不支持就直接拒绝运行甚至用软解压方式跑性能和CPU占用率都会异常低。还有“kmp external codec libvlcjni.so cpu arm64-v8a”这种Android端的报错本质上是so库的架构不匹配ARM64的库装到了不匹配的环境里程序要么崩溃要么只能跑兼容模式。这种情况的排查路径很简单先看报错日志如果有明显的“instruction set”“AVX”“arm64”之类的字样直接往指令集和架构方向查。查看CPU指令集支持用lscpuLinux里看flagsWindows里可以用CPU-Z。确认CPU型号后去软件官网找支持该架构的版本或者换一台支持的机器。有些时候也可以尝试设置环境变量强制软件走兼容模式但性能会打折扣。3.8 场景八程序被外部系统卡住所有线程都在等最后一种场景最隐蔽程序本身没有任何计算问题但它的业务逻辑依赖于外部系统——数据库、Redis、消息队列、上游HTTP接口。外部系统一旦变慢或不可用程序的线程就全部阻塞在等待响应上CPU自然就“闲”下来了。现象非常典型top里CPU不到5%进程线程数看起来很多但压测请求全部超时。用jstack抓线程栈你会看到大量线程都停在同一个位置——比如java.net.SocketInputStream.socketRead0或者在等待数据库连接池的连接。这类问题的定位要跳出进程本身去看依赖链路。数据库连接池有没有耗尽慢查询有没有堆积Redis的响应时间是不是飙升了上游接口是不是超时了DNS解析是不是卡住了用一个词概括链路治理。给所有外部调用设置合理的超时时间连接超时读超时加上熔断和降级确保一个依赖卡死不会拖垮整个服务。Java里用SocketTimeoutException快速失败连接池设置maxWait和testOnBorrow这是最基本的兜底手段。我遇到过最典型的一次服务CPU几乎为0但所有接口都在超时。排了半天才找到原因——上游的一个鉴权接口从100ms延迟变成了15秒所有请求都在等待鉴权返回。把超时时间从30秒改成3秒加上熔断之后CPU恢复正常业务量也上来了。记住一个公式CPU空闲 没有任务在算 任务在等待。找到那个“等待点”问题就解决了一半。4. 工具实测一套通用的定位流程与判断标准前面把场景都列了一遍接下来给一套完整的、可以照着抄的排查流程。这套流程我在线下和线上都验证过很多次基本能覆盖绝大多数“CPU占不上去”的情况。4.1 从top开始先看进程还是先看系统所有CPU问题排查第一条命令都建议是top但要学会正确地看top的输出。top第一行是系统概况load average、当前时间、登录用户数。load average是1分钟、5分钟、15分钟的平均运行队列长度它跟CPU使用率不是一回事。一台8核机器load average如果是8说明运行队列刚好饱和即使CPU使用率只有80%系统也已经处于“繁忙”状态。如果load average很低但程序很慢那更可能是程序层面的等待问题。第二行是进程/线程状态统计total、running、sleeping、stopped、zombie。重点关注running的数量如果只有一两个在跑说明并发度确实很低。第三行是CPU状态us用户态、sy内核态、ninice调整、id空闲、waIO等待、hi硬件中断、si软件中断、st被偷走的时间。这里有个快速判断的口诀us高说明在跑用户程序sy高说明系统调用/内核处理多wa高说明磁盘IO有瓶颈st高说明云主机在争抢CPU资源id高说明确实很闲。我通常还会同时跑mpstat -P ALL 1看一眼每个核的分布。单核打满其他核空闲指向并行度问题所有核都只有30%指向等待和阻塞个别核sy特别高指向锁竞争或者中断风暴。4.2 线程级定位找到谁在等、谁在跑top能告诉你“这个进程占了多少CPU”但不能告诉你“为什么占这么多”或“为什么占这么少”。这时候需要把视角降到线程级别。Linux下用pidstat -t -p PID 1可以按线程展示CPU使用率能看到进程内哪个线程真在消耗CPU。Java应用可以用jstack PID抓线程栈然后用printf %x\n PID把线程ID转成十六进制在jstack输出里找到对应线程。Python应用可以用py-spy dump --pid PID它会打印当前Python层次的调用栈这对定位GIL和阻塞问题特别有效。如果CPU总体很低但有些线程状态是RUNNABLE看它们在跑什么如果大量线程是BLOCKED或WAITING看它们在等什么。前者往往是计算问题后者往往是资源问题。4.3 实测一把一个Java进程占不上去的完整排查过程说一个我印象比较深的排查案例把整个思考链路完整还原出来。某天接到一个Java后端服务CPU占不上去的反馈。业务方说压测到5000QPS就上不去了机器是8核16GCPU使用率只有35%加了并发也没用。我登录机器后先跑了top看到的情况是us 30%sy 4%id 66%sys负载也不高整个机器的CPU确实没吃满。然后看业务进程的CPU显示是280%用了不到3个核和8核机器的容量预期差得远。接着用pidstat -t -p 12345 1看线程分布发现CPU消耗集中在少数几个线程上大部分线程都是0%。这就不对劲了一个高并发的服务线程池通常有几十个线程不可能只有这么少线程在干活。于是jstack 12345抓线程栈保存下来统计线程状态。结果让我吃了一惊线程总数200多个其中150多个线程都停在同一个地方——HikariCP的getConnection调用上等待数据库连接。也就是说服务确实在疯狂请求数据库但数据库连接池一共就20个连接大家都在排队等连接。再看数据库侧的指标活跃连接数确实一直顶着20的上限。问题定位清楚了不是CPU不够而是数据库连接池配置严重偏小所有业务线程都在等待连接CPU自然“占不上去”。解法也很直接把HikariCP的最大连接数从20调到50同时在SQL层加了索引减少单次查询时间。改完之后重新压测QPS从5000涨到了15000CPU也稳定跑到了80%以上。这个案例是典型的“CPU占不上去根因在数据库连接池”。它说明一个很重要的道理CPU是执行者不是瓶颈本身。要找到那个真正被塞住的水管CPU才有活可干。5. 对照面那些“占用过高”的反向案例能教我们什么标题虽然说的是“占不上去”但我特意留一节讲“占用过高”的反向案例。原因很简单如果你连异常高的CPU是什么样、为什么高都不知道“占不上去”的判断依据也是不牢靠的。热词里那些“服务主机dcom占用cpu高怎么解决”“comsurrogate占用cpu过高”“aceguardclient占用cpu很高”的问题从方法论上和“占不上去”是同一套都在排查进程、线程、依赖三件事。5.1 常见的“占用高”进程盘点先说服务主机DCOMWindows下的dcomhost.exe。它本身是COM组件运行时的宿主进程正常情况下CPU占用很低。一旦某个COM组件出现死循环、注入异常或者权限问题这个进程就可能疯狂占CPU。排查方向是看事件查看器里有没有组件错误日志再逐个排查安装的第三方组件。COM Surrogatedllhost.exe的原理类似它负责承载资源管理器里的扩展组件比如视频缩略图、Office预览这类东西。如果某个解码器有bug它就会反复崩溃重启CPU占用极高。处理方案是换解码器、清理缩略图缓存或者找到具体触发文件。Code Helper是VSCode的多进程辅助进程常驻多个实例。如果你装了太多扩展尤其是语法高亮、代码检查类的扩展它们会在后台疯狂扫描代码CPU自然高。排查时在VSCode里用“进程管理器”看哪个扩展的CPU高禁用掉就好。热词里的“code helper占用cpu”就是这么回事。AceGuardClient这类安全防护软件进程CPU高通常是因为扫描引擎在后台做全盘扫描或者文件监控需要调整扫描策略或者排除掉非关键目录。“asiainfo md application占cpu”则往往对应某个企业级中间件的指标采集器被宿主机的资源变化带起来本质上要看机器上有多少被监控的对象在产生指标。这些“高”和“占不上去”的另一个关系是当你把这些异常进程处理掉之后系统的CPU资源会重新变得充裕但如果你自己的业务程序仍然吃不满CPU那问题又绕回到前面的八种场景了。所以排查“占不上”之前先把机器上异常的“高占用”清理干净这是基本前提。5.2 高与不高的共同排查路径不管是“占不上去”还是“占用过高”我建议都统一走三步第一步确认进程归属。top、ps查清楚这个进程是谁、属于哪个应用、启动时间多久。Windows下用Process Explorer可以看到进程对应的命令行和父进程有助于判断是不是流氓软件。第二步看线程状态。拿到线程栈之后统计RUNNABLE、BLOCKED、WAITING三个状态的比例。RUNNABLE多说明在算BLOCKED多说明锁竞争WAITING多说明在等待IO或外部服务。第三步确认依赖链路的健康度。数据库慢查询、Redis延迟、上游接口响应时间都要一起看。很多时候进程层面的异常只是表象真正的根因在下游。这套路径不需要很高深的理论只要按顺序执行大部分问题都能定位到七七八八。6. 经验总结与速查表6.1 判断“占不上去”的决策树把前面的内容浓缩成一套可以照着做的决策逻辑第一步看整个系统层面。top里看load average和st、wa指标。如果st高那是宿主机超卖找云厂商或换实例如果wa高那是磁盘IO瓶颈优化存储层。第二步看进程的CPU使用情况。如果一个进程单核算满其他核空闲那就是程序本身并行度不足往单线程、GIL、串行逻辑方向排查。第三步看线程状态分布。大量BLOCKED是锁竞争大量WAITING是等待资源连接池、IO大量RUNNABLE但总CPU不高说明线程在自旋或忙等。第四步看外部依赖和运行环境。数据库连接池、消息队列、容器配额、CPU亲和性全部排查一遍。第五步再看指令集和架构问题。查日志里有没有AVX、ARM64之类的关键词有就检查硬件型号。6.2 常见问题速查表场景关键症状定位命令解决方向单线程/无并行度单核100%其他核0%pidstat -t -p PID拆分任务、多进程/线程/实例锁竞争BLOCKED线程多sy偏高jstack、perf top减小锁粒度、无锁化设计IO等待wa高磁盘队列深vmstat、iostat异步IO、批量读写、缓存线程池太小队列深消费线程不足中间件监控、jstack调整核心/最大线程数GC/运行时限制CPU不低但吞吐低Jstack有GC标记jstat -gcutil调堆参数、换GC算法、优化对象分配容器CPU限额容器内CPU顶满但总量不高cat /sys/fs/cgroup/cpu.max调整limits或requestsCPU亲和性进程绑核taskset -pc PID解除绑定或按NUMA布局分配指令集不匹配日志报AVX/architecture错误lscpu查看flags换硬件或换兼容软件版本外部依赖阻塞CPU极低线程全在WAITINGjstack、数据库监控设置超时、熔断、降级、连接池扩容6.3 我的实操心得与避坑清单最后分享几条我在反复排查这类问题时总结出来的心得不一定写在任何教科书里但实战价值很高。第一别一上来就怀疑CPU先用top看wa和st这两个指标。很多人花了几个小时分析程序逻辑最后发现是宿主机超卖st高或者磁盘挂了wa高程序本身一点问题都没有。第二所有压测都要先确认压测端自身不是瓶颈。我见过太多“CPU占不上”的案例最后发现是压测机自己已经打满了根本压不进去。压测机的CPU、网络、连接数都要预留余量。第三看CPU使用率要分瞬时长时。用sar或监控系统留一条历史曲线观察CPU使用率是持续低位还是周期性波动。持续低位说明有结构性瓶颈周期性波动往往是定时任务和GC在搞事。第四容器环境里一定要看cgroup配额而不是只看top。top里的CPU%有时会误导你docker stats或者cgroup的cpu.stat才是容器真实能使用的资源量。第五云主机上遇到st高别怪自己程序这是“邻居”在抢资源。Windows上可以换实例规格Linux下可以看看是不是开启了超线程导致的争抢。排查完一轮如果发现确实不是程序问题果断换机器或者找云厂商在这上面死磕没有意义。CPU占不上去这个问题本质上是一个系统工程问题涉及程序并发模型、运行时机制、底层硬件、外部依赖和运行环境五个层面。单独看任何一个层面都可能找不到答案必须逐层排查、互相印证。把上面这套方法跑一遍至少能解决九成以上的问题。剩下的那一成通常就需要strace、perf这些底层工具去做更深入的内核级分析了那就是另一篇文章的内容了。