戰(zhàn):進(jìn)程內(nèi)存占用查看與系統(tǒng)排查技巧)
排查Linux服務(wù)器問題的時(shí)候我干的第一件事永遠(yuǎn)是敲下top。這三個(gè)字母看起來簡單但它能告訴你的東西幾乎等于半臺(tái)服務(wù)器的體檢報(bào)告——哪個(gè)進(jìn)程在搶CPU誰把系統(tǒng)內(nèi)存吃掉了磁盤IO忙不忙load average是不是已經(jīng)爆了一眼就能掃出來。今天這篇文章就圍繞“top命令的使用”和“查看某個(gè)進(jìn)程占用的系統(tǒng)內(nèi)存大小”這兩條主線展開把top從啟動(dòng)到交互、從單次查看到腳本化監(jiān)控的全套玩法拆開來聊。無論你是剛接觸Linux的開發(fā)者還是天天被線上告警折磨的運(yùn)維這篇都值得存一份。文章不會(huì)只停留在“按個(gè)M看內(nèi)存排序”這種說明書層面我會(huì)把進(jìn)程和系統(tǒng)內(nèi)存之間的對應(yīng)關(guān)系、top輸出里每一列的真實(shí)含義、常見誤區(qū)和實(shí)際排查經(jīng)驗(yàn)都串起來講。畢竟工具本身很簡單真正難的是你知道自己在看什么。1. top命令使用前的核心認(rèn)知很多人一上來就敲top然后被滿屏數(shù)字嚇到。其實(shí)top的設(shè)計(jì)初衷很樸素它就是一個(gè)能持續(xù)刷新、可以交互操作的進(jìn)程實(shí)時(shí)監(jiān)視器。想用好它你得先搞清楚兩件事——top到底給你看什么以及它顯示的“內(nèi)存”到底指什么。1.1 top是什么一個(gè)能持續(xù)刷新的進(jìn)程實(shí)時(shí)監(jiān)視器Linux下監(jiān)控系統(tǒng)資源有很多命令ps、free、vmstat、htop各有各的適用場景。top最特殊的地方在于它默認(rèn)是“活的”啟動(dòng)之后每隔幾秒自動(dòng)刷新一次而且能在界面里直接排序、過濾、發(fā)信號給進(jìn)程。你想象一下ps是拿手機(jī)給體檢報(bào)告拍張照片top是直接連上心電圖機(jī)實(shí)時(shí)看波形這就是本質(zhì)區(qū)別。所以top特別適合兩個(gè)場景第一系統(tǒng)還在持續(xù)運(yùn)行中你想動(dòng)態(tài)觀察資源變化第二你已經(jīng)發(fā)現(xiàn)某個(gè)進(jìn)程或某項(xiàng)資源不對勁想在交互界面里快速展開定位。比如服務(wù)器突然CPU跑滿你用ps aux看到的只是一瞬間的狀態(tài)CPU占用率可能下一秒就變成別的進(jìn)程了。而top會(huì)一直刷新誰持續(xù)把CPU吃滿在界面里一目了然。top還有一個(gè)容易被忽略的身份批處理模式下它其實(shí)是一個(gè)非常好用的“監(jiān)控?cái)?shù)據(jù)輸出工具”。后面我會(huì)詳細(xì)講top -b -n這種用法很多自己寫的巡檢腳本、告警腳本里最核心的數(shù)據(jù)源就是從top批處理輸出里解析出來的。1.2 先把內(nèi)存指標(biāo)讀明白VIRT、RES、SHR、%MEM看進(jìn)程的內(nèi)存占用top列表里有幾列名字特別容易混淆VIRT、RES、SHR、%MEM。這四個(gè)概念不搞明白你就會(huì)被“某個(gè)進(jìn)程占了多少內(nèi)存”這個(gè)問題反復(fù)誤導(dǎo)。我用最直白的方式解釋一遍。VIRTVirtual Memory Size是進(jìn)程申請的虛擬內(nèi)存總量包括進(jìn)程自己申請的堆、棧以及它映射的共享庫、內(nèi)存映射文件等。虛擬內(nèi)存可以很大但它不代表真正占用了物理內(nèi)存。你可以把它理解成一個(gè)人“理論上需要準(zhǔn)備的辦公空間”——哪怕只是紙上規(guī)劃了一大片區(qū)域?qū)嶋H的桌子還沒鋪開。RESResident Memory Size才是進(jìn)程當(dāng)前實(shí)際駐留在物理內(nèi)存中的部分也就是真正占用了物理內(nèi)存條空間的大小。這才是“這個(gè)進(jìn)程到底吃了多少內(nèi)存”最值得看的指標(biāo)。top內(nèi)存排序和%MEM計(jì)算本質(zhì)上都是基于這個(gè)數(shù)值。SHRShared Memory Size是進(jìn)程使用的共享內(nèi)存量包括共享庫、共享內(nèi)存段等。多個(gè)進(jìn)程可以同時(shí)引用同一份共享庫代碼這份庫在物理內(nèi)存里只存一份但每個(gè)進(jìn)程的RES里都會(huì)計(jì)入自己引用的一部分。所以當(dāng)你用top按RES把所有進(jìn)程加起來再對比free顯示的總used內(nèi)存會(huì)發(fā)現(xiàn)“加起來對不上”原因之一就是SHR被重復(fù)計(jì)算了。這點(diǎn)后面的踩坑部分會(huì)再展開。%MEM就簡單了進(jìn)程的RES除以物理內(nèi)存總量再乘以100表示這個(gè)進(jìn)程占用了系統(tǒng)物理內(nèi)存的百分比。它和RES是強(qiáng)綁定的關(guān)系RES漲它就漲RES跌它就跌。2. 快速上手top的基本操作與交互快捷鍵工具不吃透快捷鍵使用效率會(huì)低一半。top啟動(dòng)很簡單終端敲top回車就行但啟動(dòng)之后的界面信息怎么讀、怎么讓它按你的思路展示數(shù)據(jù)這里面有不少門道。2.1 首次運(yùn)行top界面信息怎么讀運(yùn)行top后屏幕上半部分是匯總區(qū)下半部分是進(jìn)程列表。匯總區(qū)前幾行信息量很大。第一行依次是當(dāng)前時(shí)間、系統(tǒng)已運(yùn)行時(shí)間、登錄用戶數(shù)、系統(tǒng)負(fù)載load average的1分鐘/5分鐘/15分鐘平均值。負(fù)載值不是百分比它是一個(gè)“等待調(diào)度的進(jìn)程數(shù)正在運(yùn)行的進(jìn)程數(shù)”的加權(quán)值一般經(jīng)驗(yàn)是負(fù)載值乘以100以后和CPU核心數(shù)對比明顯大于100%才叫過載。第二行是任務(wù)匯總total進(jìn)程總數(shù)、running運(yùn)行中、sleeping睡眠、stopped停止、zombie僵尸。重點(diǎn)看zombie如果長期存在僵尸進(jìn)程通常是父進(jìn)程沒有正確回收子進(jìn)程資源。第三行是CPU狀態(tài)匯總us用戶態(tài)占用、sy內(nèi)核態(tài)占用、ni優(yōu)先級調(diào)整過的進(jìn)程占用、id空閑、wa等待IO、hi硬件中斷、si軟件中斷、st被虛擬機(jī)偷走的時(shí)間。線上排查CPU問題先看us高還是sy高us高是業(yè)務(wù)代碼在燒CPUsy高要懷疑系統(tǒng)調(diào)用頻繁或內(nèi)核線程異常wa高則說明磁盤或網(wǎng)絡(luò)IO才是瓶頸。第四行和第五行分別是物理內(nèi)存和交換分區(qū)信息total總量、free完全空閑、used已使用、buff/cache用作內(nèi)核緩沖區(qū)/頁緩存的部分。很多新手會(huì)把used直接當(dāng)成“系統(tǒng)內(nèi)存真的不夠了”其實(shí)內(nèi)核的cache是可以按需回收的真實(shí)可用內(nèi)存得看free命令里available那列或者用used減去buff/cache部分再判斷。這點(diǎn)后面我還會(huì)強(qiáng)調(diào)。2.2 交互模式下必須掌握的快捷鍵進(jìn)程列表默認(rèn)是按CPU占用率從高到低排序但實(shí)際大家更喜歡看一眼內(nèi)存占用情況。在top交互界面里按鍵瞬間生效而且不用按回車按M進(jìn)程列表按內(nèi)存占用RES從高到低排序想找“誰在吃內(nèi)存”就按這個(gè)。按P按CPU占用率從高到低排序這是默認(rèn)排序方式。按N按PID數(shù)字大小排序。按T按累計(jì)運(yùn)行時(shí)間排序適合看誰長期霸占CPU。按c顯示完整命令行很多進(jìn)程默認(rèn)只顯示名字按c之后能看到它啟動(dòng)時(shí)的完整參數(shù)定位問題特別實(shí)用。按1展開或折疊多核CPU的每個(gè)核狀態(tài)很多top版本默認(rèn)把所有核平均值合成一行。按u輸入用戶名只顯示這個(gè)用戶的進(jìn)程。按o輸入過濾表達(dá)式比按用戶名更靈活。比如輸入COMMANDjava就只看命令行里帶java的進(jìn)程。按k輸入PID后可以給進(jìn)程發(fā)送信號默認(rèn)是15SIGTERM輸入9就是強(qiáng)殺。按r修改進(jìn)程優(yōu)先級renice數(shù)值越小優(yōu)先級越高。按W保存當(dāng)前配置下次啟動(dòng)top還是這個(gè)布局。按q退出。這里我想單獨(dú)說下o過濾表達(dá)式它的能力比很多老運(yùn)維平時(shí)用到的都強(qiáng)。格式是字段值支持正則表達(dá)式比如o之后輸入%MEM5.0或者RES1048576界面里就只顯示物理內(nèi)存占用超過1GB的進(jìn)程。這個(gè)在做大內(nèi)存問題排查時(shí)非常好用不信你試試屏幕上瞬間干凈。2.3 批處理模式讓top變成可腳本化的數(shù)據(jù)源交互模式適合人來操作但如果你想把top的輸出拿去存日志、做告警或者寫進(jìn)定時(shí)任務(wù)里就需要批處理模式。命令是top -b -n 2 -d 3-b表示批處理模式不會(huì)進(jìn)入交互界面直接往標(biāo)準(zhǔn)輸出打印結(jié)果-n 2表示輸出2幀-d 3表示每幀間隔3秒。批處理模式下的輸出和交互模式基本一致只是不會(huì)刷新光標(biāo)而是逐幀打印非常適合重定向到文件里。這里有一個(gè)很多人踩過的坑top -b -n 1抓出來的第一幀CPU占用率往往是不準(zhǔn)的。因?yàn)閠op剛啟動(dòng)時(shí)還沒有足夠的時(shí)間窗口去統(tǒng)計(jì)CPU使用率第一幀顯示的是從開機(jī)到當(dāng)前的累計(jì)平均狀態(tài)而不是最近一個(gè)刷新周期的狀態(tài)。所以我自己的習(xí)慣是至少抓兩幀取第二幀。再配合| head或者awk就能從輸出中提取你需要的那部分。比如我想知道當(dāng)前有沒有進(jìn)程CPU占用超過80%可以這樣top -b -n 2 -d 3 | awk NR7 $9 80.0 {print}NR7是跳過頂部匯總信息層直接從進(jìn)程列表開始處理。實(shí)際使用時(shí)可能需要根據(jù)top版本微調(diào)行號建議先用top -b -n 1 | head -20確認(rèn)你的輸出位置。3. 鎖定單個(gè)進(jìn)程查看指定進(jìn)程內(nèi)存占用的幾種方案標(biāo)題里的核心訴求是“查看某個(gè)進(jìn)程占用的系統(tǒng)內(nèi)存大小”。top雖然默認(rèn)顯示所有進(jìn)程但真正處理問題時(shí)我們往往只關(guān)心一兩個(gè)特定的進(jìn)程比如Java服務(wù)、Nginx、MySQL。這時(shí)候全量列表太吵最好精準(zhǔn)鎖定目標(biāo)。我列幾種我自己常用的做法。3.1 方案一找到PID后用top -p進(jìn)程ID實(shí)測最直接的方式先用pgrep或ps找到進(jìn)程的PID然后top -p只看這個(gè)進(jìn)程。pgrep -f nginx: worker top -p 12345top -p后面可以跟多個(gè)PID用逗號分隔比如top -p 12345,12346。這個(gè)模式下匯總區(qū)的負(fù)載、CPU、內(nèi)存信息還在下面的進(jìn)程列表只剩你指定的那一個(gè)。如果再配合-d 1把刷新間隔調(diào)成1秒基本就是一個(gè)“單進(jìn)程監(jiān)控面板”了。在多核CPU機(jī)器上如果這個(gè)進(jìn)程是多線程的你可能會(huì)困惑為什么這一行整體CPU占用率那么高——因?yàn)閠op默認(rèn)顯示的是進(jìn)程所有線程在物理核上的累計(jì)占用。想要更細(xì)的線程視角可以加-H參數(shù)這個(gè)后面單獨(dú)講。3.2 方案二在top交互界面里按條件過濾如果已經(jīng)在top的全量界面里了不想退出去再輸一遍命令可以用過濾功能精準(zhǔn)鎖定目標(biāo)。按o輸入過濾表達(dá)式例如COMMANDjava只顯示命令行里含java的進(jìn)程。也可以組合條件比如只看某個(gè)用戶的Java進(jìn)程USERroot再配合M鍵按內(nèi)存排序很快就能從一堆進(jìn)程里挑出目標(biāo)。如果只想看某個(gè)特定PIDtop默認(rèn)沒有“按PID顯示”的快捷鍵但可以用過濾表達(dá)式PID12345這是我平時(shí)最常用的方式。過濾時(shí)還能按等號精確匹配、按!排除功能相當(dāng)靈活。注意過濾表達(dá)式在輸入o之后出現(xiàn)在屏幕底部的行列里格式要嚴(yán)格按照字段值來。3.3 方案三按進(jìn)程名動(dòng)態(tài)定位后進(jìn)入top有些場景下PID會(huì)隨時(shí)變化比如每次重啟服務(wù)PID都不同不適合寫死。這時(shí)可以用命令替換把pgrep找出來的PID直接傳給top。top -p $(pgrep -f your_service_name)如果匹配到多個(gè)PIDpgrep默認(rèn)每行一個(gè)需要轉(zhuǎn)成英文逗號分隔可以這樣top -p $(pgrep -f your_service_name | paste -sd,)我自己在調(diào)試復(fù)雜Java應(yīng)用時(shí)經(jīng)常這么干。輸入不算短但好處是即使應(yīng)用重啟只要命令行特征沒變執(zhí)行一次命令就能自動(dòng)鎖到新PID上。3.4 補(bǔ)充思路用ps先排序再逐個(gè)確認(rèn)top適合動(dòng)態(tài)觀察但如果你的目的是“立刻列出內(nèi)存占用最高的幾個(gè)進(jìn)程”ps配合排序更高效ps aux --sort-%mem | head -20這行命令會(huì)把內(nèi)存占用率最高的前20個(gè)進(jìn)程列出來。先全局掃一遍把懷疑對象的PID記住再進(jìn)top用-p做定點(diǎn)監(jiān)控效率最高。記住一個(gè)原則ps負(fù)責(zé)快速采樣top負(fù)責(zé)持續(xù)觀察兩個(gè)工具是互補(bǔ)關(guān)系不是替代關(guān)系。3.5 連續(xù)采樣判斷內(nèi)存趨勢與內(nèi)存泄漏單次看到某個(gè)進(jìn)程RES很高說明它當(dāng)前占得多但要判斷是不是內(nèi)存泄漏必須看趨勢。我遇到過一個(gè)典型場景一個(gè)Java服務(wù)跑著跑著RES從2GB漲到8GB業(yè)務(wù)量卻沒變化最后確認(rèn)是某個(gè)緩存列表不斷追加沒做清理。排查趨勢的土辦法就是連續(xù)采樣把每個(gè)時(shí)間點(diǎn)的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取第二幀數(shù)據(jù)管道交給awk過濾出目標(biāo)PID所在行再打上時(shí)間戳。跑幾分鐘后你就能看到某個(gè)進(jìn)程的RES曲線。如果它持續(xù)上漲且永遠(yuǎn)不回落內(nèi)存泄漏的嫌疑就非常大如果漲到一定程度趨于平穩(wěn)可能只是緩存池在預(yù)熱。4. 實(shí)戰(zhàn)場景CPU告警與內(nèi)存異常時(shí)的完整排查流程工具練完得上真戰(zhàn)場。這里我把一次典型的線上故障排查過程拆解出來你會(huì)看到top、ps、free、/proc是怎么配合使用真正把“某個(gè)進(jìn)程占了多少系統(tǒng)內(nèi)存”查清楚的。4.1 典型流程從全局到單進(jìn)程的五步定位法第一步先用free -h和uptime看整體。如果available內(nèi)存很低或者load average三個(gè)值都在飆升說明系統(tǒng)資源出狀況了。第二步打開top按M排序看是哪個(gè)進(jìn)程的RES沖在最前面按P排序看CPU占用又是誰最猛。很多時(shí)候這兩個(gè)排序結(jié)果不一致——比如MySQL內(nèi)存占第一但CPU高的是另一個(gè)查詢進(jìn)程這時(shí)你就要先把兩邊的依賴關(guān)系理出來。第三步找到嫌疑進(jìn)程的PID用top -p PID做定點(diǎn)監(jiān)控。第四步結(jié)合/proc/PID/status等文件看進(jìn)程內(nèi)部細(xì)節(jié)。第五步根據(jù)結(jié)論處理——重啟服務(wù)、調(diào)整配置、優(yōu)化代碼或者只是臨時(shí)用renice降一下優(yōu)先級給核心業(yè)務(wù)騰資源。這套流程核心就一個(gè)思想先全局、后局部先現(xiàn)象、后原因。誰也不會(huì)一上來就盯著某個(gè)進(jìn)程看半天全局排序能幫你迅速縮小懷疑范圍。4.2 用/proc深入進(jìn)程內(nèi)部看內(nèi)存細(xì)節(jié)top提供了“進(jìn)程占了多少內(nèi)存”的結(jié)論但進(jìn)程內(nèi)部到底是哪塊內(nèi)存吃得最多top說不清楚。這時(shí)就要看/proc文件系統(tǒng)這是Linux內(nèi)核暴露給用戶態(tài)的“進(jìn)程檔案柜”。grep -E VmRSS|VmSize|Threads /proc/12345/statusVmRSS就是和top里RES對應(yīng)的物理內(nèi)存大小單位是KBVmSize對應(yīng)VIRTThreads是線程數(shù)。想看更細(xì)的內(nèi)存區(qū)域分布用pmappmap -x 12345pmap會(huì)把進(jìn)程地址空間里的每一段內(nèi)存映射列出來比如堆heap、棧stack、共享庫、匿名映射等每段占多大都能看到。這個(gè)命令在定位“進(jìn)程為什么內(nèi)存高”時(shí)特別有用能看出是堆外內(nèi)存失控還是某個(gè)共享庫映射了超大文件還是線程棧累積太多導(dǎo)致的虛擬內(nèi)存膨脹。另外還有一個(gè)很實(shí)用的檢查看進(jìn)程打開了多少文件描述符。ls /proc/12345/fd | wc -l文件描述符泄漏同樣會(huì)導(dǎo)致內(nèi)存異常特別是連接型服務(wù)。我曾經(jīng)見過一個(gè)API網(wǎng)關(guān)進(jìn)程因?yàn)檫B接池沒釋放fd數(shù)量一路飆升到幾萬個(gè)RES也同步漲上去最后靠觀察fd數(shù)量變化才定位到根因。4.3 監(jiān)控腳本實(shí)例自動(dòng)記錄某個(gè)進(jìn)程的資源占用如果總不能讓值班同事手動(dòng)刷新top那就寫個(gè)簡單的監(jiān)控腳本把數(shù)據(jù)自動(dòng)落盤。下面是我用過的一個(gè)精簡版適合跟蹤單個(gè)進(jìn)程#!/bin/bash # 監(jiān)控指定進(jìn)程的CPU和內(nèi)存占用追加寫入日志 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運(yùn)行方式chmod x monitor_proc.sh ./monitor_proc.sh 12345 /tmp/my_service.log原理沒有太高深的地方核心調(diào)用top -b -n 2 -d 1 -p然后用awk精確匹配PID行提取第9列CPU%、第10列MEM%、第6列RESKB、第5列VIRTKB打時(shí)間戳后追加寫入文件。腳本留了5秒間隔避免日志漲太快。需要停止時(shí)用CtrlC或者配合nohup放后臺(tái)長期跑。這個(gè)腳本價(jià)值在于等問題復(fù)現(xiàn)時(shí)你手里有一份時(shí)間線完整的數(shù)據(jù)文件能直接畫出內(nèi)存趨勢圖比事后猜測強(qiáng)太多。4.4 線程級視角用top -H看到進(jìn)程內(nèi)部的線程top默認(rèn)顯示進(jìn)程維度但某些場景下進(jìn)程整體CPU不高問題出在某個(gè)線程上。尤其是Java這種多線程應(yīng)用一個(gè)進(jìn)程可能開了幾十個(gè)線程某個(gè)線程死循環(huán)燒核從進(jìn)程維度看CPU總和高但不知道具體是哪個(gè)線程。這時(shí)用top -H -p PIDtop會(huì)把該進(jìn)程的所有線程逐條列出來每行是一個(gè)線程PID列顯示的是TID線程ID。先找到CPU占用高的那個(gè)線程ID再將它轉(zhuǎn)成十六進(jìn)制printf %x\n 12345然后拿到該線程的堆?;蛘叻治鋈罩揪湍芫_定位到代碼層面。這個(gè)方法我在排查Java應(yīng)用線程死循環(huán)、線程阻塞等問題時(shí)用過很多次效率極高。同樣top -H也支持-b批處理模式可以腳本化采集線程級數(shù)據(jù)。5. 踩坑記錄與常見問題速查工具本身不難難的是輸出結(jié)果的解讀。我把這幾年用過top之后踩過的坑、誤判過的數(shù)據(jù)總結(jié)出來希望你看完能避開這些習(xí)慣性誤區(qū)。5.1 top結(jié)果顯示的內(nèi)存不等于真實(shí)物理內(nèi)存占用最典型的誤解是看到某個(gè)進(jìn)程VIRT十幾個(gè)GB就斷定“服務(wù)器內(nèi)存爆了”。VIRT包含的是虛擬地址空間進(jìn)程申請過的虛擬內(nèi)存不一定全部映射到物理內(nèi)存。Java進(jìn)程的堆即使預(yù)分配了8GB實(shí)際RES可能只有2GB因?yàn)槲词褂貌糠值奈锢眄摳緵]有分配。判斷物理內(nèi)存占用永遠(yuǎn)以RES和%MEM為主要依據(jù)。另一個(gè)坑是SHR共享內(nèi)存重復(fù)計(jì)算。用top把所有進(jìn)程的RES加總想和物理內(nèi)存總量做對比你會(huì)發(fā)現(xiàn)數(shù)值“超了”。原因就是共享庫、共享內(nèi)存段被多個(gè)進(jìn)程分別計(jì)入自己的RES里。所以我不建議用“進(jìn)程RES求和”的方式來核對系統(tǒng)內(nèi)存總量它只能用來大致比較進(jìn)程之間的相對占用大小。5.2 free里available和used為什么對不上很多新手看到free輸出used很高就慌了結(jié)果系統(tǒng)其實(shí)一切正常。原因是Linux會(huì)把空閑內(nèi)存盡量用作文件緩存cache這是內(nèi)核主動(dòng)利用閑置內(nèi)存提升IO性能的行為不屬于“不可回收占用”。判斷內(nèi)存是否緊張應(yīng)該看available這一列它表示“在不觸發(fā)明顯swap的前提下還能分配給新進(jìn)程多少內(nèi)存”比free列更有參考價(jià)值。結(jié)合起來用top時(shí)如果全進(jìn)程RES加起來并不高但free顯示used很高很可能是cache在漲而不是哪個(gè)業(yè)務(wù)進(jìn)程真的在“泄漏”。這種情況通常是大量文件讀寫導(dǎo)致的頁緩存增長系統(tǒng)壓力大時(shí)內(nèi)核會(huì)自動(dòng)回收一般不需要人工干預(yù)。5.3 常見問題速查表遇到下面這些場景時(shí)可以直接對照排查方向?,F(xiàn)象可能原因快速排查命令處理方向CPU整體跑滿但進(jìn)程列表看不到單一高占用進(jìn)程大量短生命周期進(jìn)程或頻繁上下文切換top -H、vmstat 1檢查短進(jìn)程來源限制并發(fā)Java進(jìn)程RES持續(xù)上漲不回落堆內(nèi)存或堆外內(nèi)存泄漏jmap -heap、pmap -x抓堆轉(zhuǎn)儲(chǔ)定位引用泄漏內(nèi)存很高但free available很低頁緩存占用或確實(shí)內(nèi)存緊張cat /proc/meminfo觀察cache、swap變化趨勢磁盤IO等待wa特別高進(jìn)程在瘋狂讀寫磁盤而非計(jì)算密集iotop、top里按左箭頭切到IO列排查具體IO讀寫進(jìn)程進(jìn)程列表中RES和VIRT都很大可能是大文件mmap或堆預(yù)分配pmap -x PID確認(rèn)哪段映射最大5.4 最后再分享兩個(gè)實(shí)戰(zhàn)小技巧第一個(gè)技巧top的-d可以控制刷新間隔但設(shè)置太小比如0.1秒反而容易讓數(shù)據(jù)波動(dòng)劇烈一般生產(chǎn)環(huán)境用-d 2或-d 3比較合理。如果機(jī)器核數(shù)特別多我還會(huì)配合排序快捷鍵快速找到真正出問題的進(jìn)程。第二個(gè)技巧如果遇到“進(jìn)程明明還在但top只有CPU高、RES不高”的情況要多考慮是不是短連接風(fēng)暴或者挖礦腳本反復(fù)拉起新進(jìn)程。top里不斷出現(xiàn)陌生進(jìn)程名并很快消失就要懷疑是定時(shí)任務(wù)或惡意腳本在作怪crontab -l、檢查/tmp目錄下的可執(zhí)行文件都值得做一遍。我的個(gè)人習(xí)慣是把top當(dāng)作第一手的現(xiàn)場工具遇到任何系統(tǒng)異常都先進(jìn)top看一眼但等到了深挖階段一定把ps、free、/proc、pmap這些工具串起來綜合判斷。工具雖小組合起來才是完整的排查體系。