Linux 线上排查命令
凌晨告警,CPU 100%,群里 @ 你。登进服务器的那十分钟最考验人,命令都会敲,流程乱了就是瞎撞。CPU 飙高和内存飙高是两大现场,各有一条固定路线,背熟路线比背命令更有用。
两条路线
#mermaid-svg-RxMWIC3l1j3lWu7n{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RxMWIC3l1j3lWu7n .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RxMWIC3l1j3lWu7n .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RxMWIC3l1j3lWu7n .error-icon{fill:#552222;}#mermaid-svg-RxMWIC3l1j3lWu7n .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RxMWIC3l1j3lWu7n .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RxMWIC3l1j3lWu7n .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RxMWIC3l1j3lWu7n .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RxMWIC3l1j3lWu7n .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RxMWIC3l1j3lWu7n .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RxMWIC3l1j3lWu7n .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RxMWIC3l1j3lWu7n .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RxMWIC3l1j3lWu7n .marker.cross{stroke:#333333;}#mermaid-svg-RxMWIC3l1j3lWu7n svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RxMWIC3l1j3lWu7n p{margin:0;}#mermaid-svg-RxMWIC3l1j3lWu7n .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RxMWIC3l1j3lWu7n .cluster-label text{fill:#333;}#mermaid-svg-RxMWIC3l1j3lWu7n .cluster-label span{color:#333;}#mermaid-svg-RxMWIC3l1j3lWu7n .cluster-label span p{background-color:transparent;}#mermaid-svg-RxMWIC3l1j3lWu7n .label text,#mermaid-svg-RxMWIC3l1j3lWu7n span{fill:#333;color:#333;}#mermaid-svg-RxMWIC3l1j3lWu7n .node rect,#mermaid-svg-RxMWIC3l1j3lWu7n .node circle,#mermaid-svg-RxMWIC3l1j3lWu7n .node ellipse,#mermaid-svg-RxMWIC3l1j3lWu7n .node polygon,#mermaid-svg-RxMWIC3l1j3lWu7n .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RxMWIC3l1j3lWu7n .rough-node .label text,#mermaid-svg-RxMWIC3l1j3lWu7n .node .label text,#mermaid-svg-RxMWIC3l1j3lWu7n .image-shape .label,#mermaid-svg-RxMWIC3l1j3lWu7n .icon-shape .label{text-anchor:middle;}#mermaid-svg-RxMWIC3l1j3lWu7n .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RxMWIC3l1j3lWu7n .rough-node .label,#mermaid-svg-RxMWIC3l1j3lWu7n .node .label,#mermaid-svg-RxMWIC3l1j3lWu7n .image-shape .label,#mermaid-svg-RxMWIC3l1j3lWu7n .icon-shape .label{text-align:center;}#mermaid-svg-RxMWIC3l1j3lWu7n .node.clickable{cursor:pointer;}#mermaid-svg-RxMWIC3l1j3lWu7n .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RxMWIC3l1j3lWu7n .arrowheadPath{fill:#333333;}#mermaid-svg-RxMWIC3l1j3lWu7n .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RxMWIC3l1j3lWu7n .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RxMWIC3l1j3lWu7n .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RxMWIC3l1j3lWu7n .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RxMWIC3l1j3lWu7n .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RxMWIC3l1j3lWu7n .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RxMWIC3l1j3lWu7n .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RxMWIC3l1j3lWu7n .cluster text{fill:#333;}#mermaid-svg-RxMWIC3l1j3lWu7n .cluster span{color:#333;}#mermaid-svg-RxMWIC3l1j3lWu7n div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RxMWIC3l1j3lWu7n .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RxMWIC3l1j3lWu7n rect.text{fill:none;stroke-width:0;}#mermaid-svg-RxMWIC3l1j3lWu7n .icon-shape,#mermaid-svg-RxMWIC3l1j3lWu7n .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RxMWIC3l1j3lWu7n .icon-shape p,#mermaid-svg-RxMWIC3l1j3lWu7n .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RxMWIC3l1j3lWu7n .icon-shape .label rect,#mermaid-svg-RxMWIC3l1j3lWu7n .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RxMWIC3l1j3lWu7n .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RxMWIC3l1j3lWu7n .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RxMWIC3l1j3lWu7n :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CPU 高
内存高
告警:服务器异常
CPU 高还是内存高
top 找进程 → top -Hp 找线程 → jstack 看栈
top 看 RES → jmap 看堆 → 堆 dump 找大对象
栈里的死循环或锁等待,就是病因
dump 里的大对象或集合膨胀,就是病因
CPU 飙高:三步定位
第一步,top 全局看。top,按 P 按 CPU 排序,记下吃 CPU 的进程 PID。顺手先看 load average 和核数的关系,load 远超核数说明排队严重。
第二步,top -Hp 找线程。top -Hp <pid>,按 P 排序,找到吃 CPU 的线程 TID,用 printf '%x' <tid> 转成十六进制,这一步是为了对上 jstack 的输出。
第三步,jstack 看栈。jstack <pid> | grep <十六进制 nid> -A 10,线程栈里那几行就是案发现场。重点看两种典型:RUNNABLE 里的 while 循环是死循环烧 CPU;BLOCKED 或 waiting on 是锁竞争,看它在等哪个 monitor。
顺手看一眼 jstack <pid> 末尾有没有 found deadlock,JVM 帮你把死锁都算好了。
内存飙高:两步看
top 看进程的 RES 列是不是远超堆配置,超了可能是堆外(线程、DirectBuffer、Metaspace);再用 jmap -heap <pid> 看堆的分代占用,新生代、老年代谁满,方向就出来了。老年代持续上涨不回收,就是泄漏的形状,进一步用 jmap -dump:format=b,file=heap.hprof <pid> 拉堆 dump,用 MAT 或 VisualVM 找占内存最大的对象。这条线和 JVM 篇、导出 Excel 篇是同一个故事的现场版。
Arthas:免重启的手术刀
jstack 和 jmap 要现场,Arthas 能在线上直接看方法级的细节,不用重启不加代码。trace com.example.Service method 看方法内部每步耗时,watch com.example.Service method '{params, returnObj}' 看实时入参出参,jad 反编译确认线上跑的到底是不是你以为的版本。版本排查(发布没生效)用 jad 一锤定音。
自查实验
下面用一段 Java 起一个死循环线程占满一个核,把上文的三步流程在自己机器上走一遍。直接运行 java CpuHog.java(JDK 11+),另开一个终端照提示排查。
java
// 运行:java CpuHog.java
// 死循环线程烧 CPU,另开终端按三步流程拿它练手。
public class CpuHog {
static volatile boolean stop = false;
public static void main(String[] args) throws Exception {
Thread hog = new Thread(() -> {
long x = 0;
while (!stop) {
x++;
}
System.out.println("hog 退出,x = " + x);
}, "hog-thread");
hog.start();
long pid = ProcessHandle.current().pid();
System.out.println("进程 " + pid + " 已启动,另一个终端照流程排查:");
System.out.println(" 1) top -Hp " + pid);
System.out.println(" 2) printf '%x' <线程tid> 拿十六进制");
System.out.println(" 3) jstack " + pid + " | grep <十六进制nid> -A 10");
System.out.println("按回车停止...");
System.in.read();
stop = true;
hog.join();
System.out.println("干净退出。");
}
}
jstack 的输出里,hog-thread 的状态是 RUNNABLE,栈帧停在 CpuHog 的 while 那一行,这就是死循环在栈上的形状。记住一句话:CPU 飙高先找线程,内存飙高先找对象。
适用边界
这一篇讲 JVM 进程的现场排查。网络排查(tcpdump、ss)、磁盘和句柄(df、lsof)、以及监控体系(Prometheus 那套)是另外的题。jmap 打 dump 有短暂卡顿,流量高峰慎用;Arthas 在容器里跑要进对容器的命名空间。练完自查实验,把这份路线存成手机备忘录,下次告警时不用现想。