ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux top命令实战:进程内存占用查看与系统排查技巧

Linux top命令实战:进程内存占用查看与系统排查技巧 排查Linux服务器问题的时候我干的第一件事永远是敲下top。这三个字母看起来简单但它能告诉你的东西几乎等于半台服务器的体检报告——哪个进程在抢CPU谁把系统内存吃掉了磁盘IO忙不忙load average是不是已经爆了一眼就能扫出来。今天这篇文章就围绕“top命令的使用”和“查看某个进程占用的系统内存大小”这两条主线展开把top从启动到交互、从单次查看到脚本化监控的全套玩法拆开来聊。无论你是刚接触Linux的开发者还是天天被线上告警折磨的运维这篇都值得存一份。文章不会只停留在“按个M看内存排序”这种说明书层面我会把进程和系统内存之间的对应关系、top输出里每一列的真实含义、常见误区和实际排查经验都串起来讲。毕竟工具本身很简单真正难的是你知道自己在看什么。1. top命令使用前的核心认知很多人一上来就敲top然后被满屏数字吓到。其实top的设计初衷很朴素它就是一个能持续刷新、可以交互操作的进程实时监视器。想用好它你得先搞清楚两件事——top到底给你看什么以及它显示的“内存”到底指什么。1.1 top是什么一个能持续刷新的进程实时监视器Linux下监控系统资源有很多命令ps、free、vmstat、htop各有各的适用场景。top最特殊的地方在于它默认是“活的”启动之后每隔几秒自动刷新一次而且能在界面里直接排序、过滤、发信号给进程。你想象一下ps是拿手机给体检报告拍张照片top是直接连上心电图机实时看波形这就是本质区别。所以top特别适合两个场景第一系统还在持续运行中你想动态观察资源变化第二你已经发现某个进程或某项资源不对劲想在交互界面里快速展开定位。比如服务器突然CPU跑满你用ps aux看到的只是一瞬间的状态CPU占用率可能下一秒就变成别的进程了。而top会一直刷新谁持续把CPU吃满在界面里一目了然。top还有一个容易被忽略的身份批处理模式下它其实是一个非常好用的“监控数据输出工具”。后面我会详细讲top -b -n这种用法很多自己写的巡检脚本、告警脚本里最核心的数据源就是从top批处理输出里解析出来的。1.2 先把内存指标读明白VIRT、RES、SHR、%MEM看进程的内存占用top列表里有几列名字特别容易混淆VIRT、RES、SHR、%MEM。这四个概念不搞明白你就会被“某个进程占了多少内存”这个问题反复误导。我用最直白的方式解释一遍。VIRTVirtual Memory Size是进程申请的虚拟内存总量包括进程自己申请的堆、栈以及它映射的共享库、内存映射文件等。虚拟内存可以很大但它不代表真正占用了物理内存。你可以把它理解成一个人“理论上需要准备的办公空间”——哪怕只是纸上规划了一大片区域实际的桌子还没铺开。RESResident Memory Size才是进程当前实际驻留在物理内存中的部分也就是真正占用了物理内存条空间的大小。这才是“这个进程到底吃了多少内存”最值得看的指标。top内存排序和%MEM计算本质上都是基于这个数值。SHRShared Memory Size是进程使用的共享内存量包括共享库、共享内存段等。多个进程可以同时引用同一份共享库代码这份库在物理内存里只存一份但每个进程的RES里都会计入自己引用的一部分。所以当你用top按RES把所有进程加起来再对比free显示的总used内存会发现“加起来对不上”原因之一就是SHR被重复计算了。这点后面的踩坑部分会再展开。%MEM就简单了进程的RES除以物理内存总量再乘以100表示这个进程占用了系统物理内存的百分比。它和RES是强绑定的关系RES涨它就涨RES跌它就跌。2. 快速上手top的基本操作与交互快捷键工具不吃透快捷键使用效率会低一半。top启动很简单终端敲top回车就行但启动之后的界面信息怎么读、怎么让它按你的思路展示数据这里面有不少门道。2.1 首次运行top界面信息怎么读运行top后屏幕上半部分是汇总区下半部分是进程列表。汇总区前几行信息量很大。第一行依次是当前时间、系统已运行时间、登录用户数、系统负载load average的1分钟/5分钟/15分钟平均值。负载值不是百分比它是一个“等待调度的进程数正在运行的进程数”的加权值一般经验是负载值乘以100以后和CPU核心数对比明显大于100%才叫过载。第二行是任务汇总total进程总数、running运行中、sleeping睡眠、stopped停止、zombie僵尸。重点看zombie如果长期存在僵尸进程通常是父进程没有正确回收子进程资源。第三行是CPU状态汇总us用户态占用、sy内核态占用、ni优先级调整过的进程占用、id空闲、wa等待IO、hi硬件中断、si软件中断、st被虚拟机偷走的时间。线上排查CPU问题先看us高还是sy高us高是业务代码在烧CPUsy高要怀疑系统调用频繁或内核线程异常wa高则说明磁盘或网络IO才是瓶颈。第四行和第五行分别是物理内存和交换分区信息total总量、free完全空闲、used已使用、buff/cache用作内核缓冲区/页缓存的部分。很多新手会把used直接当成“系统内存真的不够了”其实内核的cache是可以按需回收的真实可用内存得看free命令里available那列或者用used减去buff/cache部分再判断。这点后面我还会强调。2.2 交互模式下必须掌握的快捷键进程列表默认是按CPU占用率从高到低排序但实际大家更喜欢看一眼内存占用情况。在top交互界面里按键瞬间生效而且不用按回车按M进程列表按内存占用RES从高到低排序想找“谁在吃内存”就按这个。按P按CPU占用率从高到低排序这是默认排序方式。按N按PID数字大小排序。按T按累计运行时间排序适合看谁长期霸占CPU。按c显示完整命令行很多进程默认只显示名字按c之后能看到它启动时的完整参数定位问题特别实用。按1展开或折叠多核CPU的每个核状态很多top版本默认把所有核平均值合成一行。按u输入用户名只显示这个用户的进程。按o输入过滤表达式比按用户名更灵活。比如输入COMMANDjava就只看命令行里带java的进程。按k输入PID后可以给进程发送信号默认是15SIGTERM输入9就是强杀。按r修改进程优先级renice数值越小优先级越高。按W保存当前配置下次启动top还是这个布局。按q退出。这里我想单独说下o过滤表达式它的能力比很多老运维平时用到的都强。格式是字段值支持正则表达式比如o之后输入%MEM5.0或者RES1048576界面里就只显示物理内存占用超过1GB的进程。这个在做大内存问题排查时非常好用不信你试试屏幕上瞬间干净。2.3 批处理模式让top变成可脚本化的数据源交互模式适合人来操作但如果你想把top的输出拿去存日志、做告警或者写进定时任务里就需要批处理模式。命令是top -b -n 2 -d 3-b表示批处理模式不会进入交互界面直接往标准输出打印结果-n 2表示输出2帧-d 3表示每帧间隔3秒。批处理模式下的输出和交互模式基本一致只是不会刷新光标而是逐帧打印非常适合重定向到文件里。这里有一个很多人踩过的坑top -b -n 1抓出来的第一帧CPU占用率往往是不准的。因为top刚启动时还没有足够的时间窗口去统计CPU使用率第一帧显示的是从开机到当前的累计平均状态而不是最近一个刷新周期的状态。所以我自己的习惯是至少抓两帧取第二帧。再配合| head或者awk就能从输出中提取你需要的那部分。比如我想知道当前有没有进程CPU占用超过80%可以这样top -b -n 2 -d 3 | awk NR7 $9 80.0 {print}NR7是跳过顶部汇总信息层直接从进程列表开始处理。实际使用时可能需要根据top版本微调行号建议先用top -b -n 1 | head -20确认你的输出位置。3. 锁定单个进程查看指定进程内存占用的几种方案标题里的核心诉求是“查看某个进程占用的系统内存大小”。top虽然默认显示所有进程但真正处理问题时我们往往只关心一两个特定的进程比如Java服务、Nginx、MySQL。这时候全量列表太吵最好精准锁定目标。我列几种我自己常用的做法。3.1 方案一找到PID后用top -p进程ID实测最直接的方式先用pgrep或ps找到进程的PID然后top -p只看这个进程。pgrep -f nginx: worker top -p 12345top -p后面可以跟多个PID用逗号分隔比如top -p 12345,12346。这个模式下汇总区的负载、CPU、内存信息还在下面的进程列表只剩你指定的那一个。如果再配合-d 1把刷新间隔调成1秒基本就是一个“单进程监控面板”了。在多核CPU机器上如果这个进程是多线程的你可能会困惑为什么这一行整体CPU占用率那么高——因为top默认显示的是进程所有线程在物理核上的累计占用。想要更细的线程视角可以加-H参数这个后面单独讲。3.2 方案二在top交互界面里按条件过滤如果已经在top的全量界面里了不想退出去再输一遍命令可以用过滤功能精准锁定目标。按o输入过滤表达式例如COMMANDjava只显示命令行里含java的进程。也可以组合条件比如只看某个用户的Java进程USERroot再配合M键按内存排序很快就能从一堆进程里挑出目标。如果只想看某个特定PIDtop默认没有“按PID显示”的快捷键但可以用过滤表达式PID12345这是我平时最常用的方式。过滤时还能按等号精确匹配、按!排除功能相当灵活。注意过滤表达式在输入o之后出现在屏幕底部的行列里格式要严格按照字段值来。3.3 方案三按进程名动态定位后进入top有些场景下PID会随时变化比如每次重启服务PID都不同不适合写死。这时可以用命令替换把pgrep找出来的PID直接传给top。top -p $(pgrep -f your_service_name)如果匹配到多个PIDpgrep默认每行一个需要转成英文逗号分隔可以这样top -p $(pgrep -f your_service_name | paste -sd,)我自己在调试复杂Java应用时经常这么干。输入不算短但好处是即使应用重启只要命令行特征没变执行一次命令就能自动锁到新PID上。3.4 补充思路用ps先排序再逐个确认top适合动态观察但如果你的目的是“立刻列出内存占用最高的几个进程”ps配合排序更高效ps aux --sort-%mem | head -20这行命令会把内存占用率最高的前20个进程列出来。先全局扫一遍把怀疑对象的PID记住再进top用-p做定点监控效率最高。记住一个原则ps负责快速采样top负责持续观察两个工具是互补关系不是替代关系。3.5 连续采样判断内存趋势与内存泄漏单次看到某个进程RES很高说明它当前占得多但要判断是不是内存泄漏必须看趋势。我遇到过一个典型场景一个Java服务跑着跑着RES从2GB涨到8GB业务量却没变化最后确认是某个缓存列表不断追加没做清理。排查趋势的土办法就是连续采样把每个时间点的RES记下来。for i in {1..60}; do top -b -n 2 -d 1 -p 12345 | awk $112345 {print strftime(%F %T), $0} | tail -1 sleep 5 done这段脚本的原理是top -b -n 2 -d 1 -p取第二帧数据管道交给awk过滤出目标PID所在行再打上时间戳。跑几分钟后你就能看到某个进程的RES曲线。如果它持续上涨且永远不回落内存泄漏的嫌疑就非常大如果涨到一定程度趋于平稳可能只是缓存池在预热。4. 实战场景CPU告警与内存异常时的完整排查流程工具练完得上真战场。这里我把一次典型的线上故障排查过程拆解出来你会看到top、ps、free、/proc是怎么配合使用真正把“某个进程占了多少系统内存”查清楚的。4.1 典型流程从全局到单进程的五步定位法第一步先用free -h和uptime看整体。如果available内存很低或者load average三个值都在飙升说明系统资源出状况了。第二步打开top按M排序看是哪个进程的RES冲在最前面按P排序看CPU占用又是谁最猛。很多时候这两个排序结果不一致——比如MySQL内存占第一但CPU高的是另一个查询进程这时你就要先把两边的依赖关系理出来。第三步找到嫌疑进程的PID用top -p PID做定点监控。第四步结合/proc/PID/status等文件看进程内部细节。第五步根据结论处理——重启服务、调整配置、优化代码或者只是临时用renice降一下优先级给核心业务腾资源。这套流程核心就一个思想先全局、后局部先现象、后原因。谁也不会一上来就盯着某个进程看半天全局排序能帮你迅速缩小怀疑范围。4.2 用/proc深入进程内部看内存细节top提供了“进程占了多少内存”的结论但进程内部到底是哪块内存吃得最多top说不清楚。这时就要看/proc文件系统这是Linux内核暴露给用户态的“进程档案柜”。grep -E VmRSS|VmSize|Threads /proc/12345/statusVmRSS就是和top里RES对应的物理内存大小单位是KBVmSize对应VIRTThreads是线程数。想看更细的内存区域分布用pmappmap -x 12345pmap会把进程地址空间里的每一段内存映射列出来比如堆heap、栈stack、共享库、匿名映射等每段占多大都能看到。这个命令在定位“进程为什么内存高”时特别有用能看出是堆外内存失控还是某个共享库映射了超大文件还是线程栈累积太多导致的虚拟内存膨胀。另外还有一个很实用的检查看进程打开了多少文件描述符。ls /proc/12345/fd | wc -l文件描述符泄漏同样会导致内存异常特别是连接型服务。我曾经见过一个API网关进程因为连接池没释放fd数量一路飙升到几万个RES也同步涨上去最后靠观察fd数量变化才定位到根因。4.3 监控脚本实例自动记录某个进程的资源占用如果总不能让值班同事手动刷新top那就写个简单的监控脚本把数据自动落盘。下面是我用过的一个精简版适合跟踪单个进程#!/bin/bash # 监控指定进程的CPU和内存占用追加写入日志 PID$1 LOG${2:-/tmp/top_monitor.log} echo time_pid_cpu%_mem%_res_kb_virt_kb $LOG while true; do top -b -n 2 -d 1 -p $PID | awk -v pid$PID $1 pid { printf %s %d %s %s %s %s\n, strftime(%F %T), $1, $9, $10, $6, $5 exit } $LOG sleep 5 done运行方式chmod x monitor_proc.sh ./monitor_proc.sh 12345 /tmp/my_service.log原理没有太高深的地方核心调用top -b -n 2 -d 1 -p然后用awk精确匹配PID行提取第9列CPU%、第10列MEM%、第6列RESKB、第5列VIRTKB打时间戳后追加写入文件。脚本留了5秒间隔避免日志涨太快。需要停止时用CtrlC或者配合nohup放后台长期跑。这个脚本价值在于等问题复现时你手里有一份时间线完整的数据文件能直接画出内存趋势图比事后猜测强太多。4.4 线程级视角用top -H看到进程内部的线程top默认显示进程维度但某些场景下进程整体CPU不高问题出在某个线程上。尤其是Java这种多线程应用一个进程可能开了几十个线程某个线程死循环烧核从进程维度看CPU总和高但不知道具体是哪个线程。这时用top -H -p PIDtop会把该进程的所有线程逐条列出来每行是一个线程PID列显示的是TID线程ID。先找到CPU占用高的那个线程ID再将它转成十六进制printf %x\n 12345然后拿到该线程的堆栈或者分析日志就能精确定位到代码层面。这个方法我在排查Java应用线程死循环、线程阻塞等问题时用过很多次效率极高。同样top -H也支持-b批处理模式可以脚本化采集线程级数据。5. 踩坑记录与常见问题速查工具本身不难难的是输出结果的解读。我把这几年用过top之后踩过的坑、误判过的数据总结出来希望你看完能避开这些习惯性误区。5.1 top结果显示的内存不等于真实物理内存占用最典型的误解是看到某个进程VIRT十几个GB就断定“服务器内存爆了”。VIRT包含的是虚拟地址空间进程申请过的虚拟内存不一定全部映射到物理内存。Java进程的堆即使预分配了8GB实际RES可能只有2GB因为未使用部分的物理页根本没有分配。判断物理内存占用永远以RES和%MEM为主要依据。另一个坑是SHR共享内存重复计算。用top把所有进程的RES加总想和物理内存总量做对比你会发现数值“超了”。原因就是共享库、共享内存段被多个进程分别计入自己的RES里。所以我不建议用“进程RES求和”的方式来核对系统内存总量它只能用来大致比较进程之间的相对占用大小。5.2 free里available和used为什么对不上很多新手看到free输出used很高就慌了结果系统其实一切正常。原因是Linux会把空闲内存尽量用作文件缓存cache这是内核主动利用闲置内存提升IO性能的行为不属于“不可回收占用”。判断内存是否紧张应该看available这一列它表示“在不触发明显swap的前提下还能分配给新进程多少内存”比free列更有参考价值。结合起来用top时如果全进程RES加起来并不高但free显示used很高很可能是cache在涨而不是哪个业务进程真的在“泄漏”。这种情况通常是大量文件读写导致的页缓存增长系统压力大时内核会自动回收一般不需要人工干预。5.3 常见问题速查表遇到下面这些场景时可以直接对照排查方向。现象可能原因快速排查命令处理方向CPU整体跑满但进程列表看不到单一高占用进程大量短生命周期进程或频繁上下文切换top -H、vmstat 1检查短进程来源限制并发Java进程RES持续上涨不回落堆内存或堆外内存泄漏jmap -heap、pmap -x抓堆转储定位引用泄漏内存很高但free available很低页缓存占用或确实内存紧张cat /proc/meminfo观察cache、swap变化趋势磁盘IO等待wa特别高进程在疯狂读写磁盘而非计算密集iotop、top里按左箭头切到IO列排查具体IO读写进程进程列表中RES和VIRT都很大可能是大文件mmap或堆预分配pmap -x PID确认哪段映射最大5.4 最后再分享两个实战小技巧第一个技巧top的-d可以控制刷新间隔但设置太小比如0.1秒反而容易让数据波动剧烈一般生产环境用-d 2或-d 3比较合理。如果机器核数特别多我还会配合排序快捷键快速找到真正出问题的进程。第二个技巧如果遇到“进程明明还在但top只有CPU高、RES不高”的情况要多考虑是不是短连接风暴或者挖矿脚本反复拉起新进程。top里不断出现陌生进程名并很快消失就要怀疑是定时任务或恶意脚本在作怪crontab -l、检查/tmp目录下的可执行文件都值得做一遍。我的个人习惯是把top当作第一手的现场工具遇到任何系统异常都先进top看一眼但等到了深挖阶段一定把ps、free、/proc、pmap这些工具串起来综合判断。工具虽小组合起来才是完整的排查体系。
返回列表