戰(zhàn)場(chǎng)景示例)
JDK 自帶工具jstat /jstack/jmap前提這些命令屬于JDK bin不是 JRE只能抓本地 Java 進(jìn)程需要知道進(jìn)程 PID查看 PID 命令jps -l最常用jpsJava Virtual Machine Process Status ToolJDK 自帶工具專(zhuān)門(mén)查看 Java 進(jìn)程不需要額外安裝。-l是 long長(zhǎng)格式作用輸出PID 主類(lèi)全限定名 /jar 完整路徑# 查看java進(jìn)程拿到pid jps -l假設(shè)示例中 Java 進(jìn)程 PID 12345一、jstatJVM 運(yùn)行狀態(tài)統(tǒng)計(jì)工具看 GC、類(lèi)加載、編譯等指標(biāo)全稱JVM Statistics Monitoring ToolJDK 自帶實(shí)時(shí)采樣監(jiān)控 JVM 運(yùn)行統(tǒng)計(jì)最常用就是看 GC 情況jstat [-options] pid [間隔ms] [采樣次數(shù)]常用 optiongcGC 統(tǒng)計(jì)最常用gccapacity堆各區(qū)域容量 當(dāng)前占用gcutilGC 百分比視圖推薦一眼看使用率gccausegcutil 最后一次 GC 原因class類(lèi)加載統(tǒng)計(jì)compilerJIT 編譯統(tǒng)計(jì)場(chǎng)景 1排查頻繁 FullGC觀察 GC 指標(biāo)最常用需求每 1000ms 輸出一次一共輸出 10 次查看 S0、S1、E、O、M 的GC 占比jstat -gcutil 12345 1000 10示例輸出S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 89.23 45.12 62.31 94.11 91.23 2145 32.123 12 2.456 34.579字段解釋S0/S1Survivor0、Survivor1 使用率EEden 區(qū)使用率O老年代 Old 使用率M元空間 Metaspace 使用率YGC年輕代 GC 次數(shù)YGCTYGC 總耗時(shí)FGCFullGC 次數(shù)FGCTFullGC 總耗時(shí)GCTGC 總耗時(shí)? 判斷點(diǎn)O 老年代持續(xù)上漲不斷觸發(fā) FGC →內(nèi)存泄漏FGC 次數(shù)持續(xù)飆升FGCT 時(shí)間變長(zhǎng) → 嚴(yán)重卡頓場(chǎng)景 2查看堆內(nèi)存各區(qū)域容量jstat -gccapacity 12345 1000 5可以看到 Eden、Survivor、Old 的最大容量、當(dāng)前容量。場(chǎng)景 3查看類(lèi)加載情況排查類(lèi)泄漏、元空間滿jstat -class 12345輸出加載類(lèi)數(shù)量、卸載數(shù)量、耗時(shí)。如果類(lèi)持續(xù)不斷加載不卸載 → 元空間溢出。二、jstack線程棧分析工具死鎖、線程阻塞、CPU 高、線程池打滿Java Stack TraceJDK 自帶工具打印 JVM 內(nèi)所有線程??煺兆饔肑ava 死鎖CPU100%線程無(wú)限循環(huán)線程阻塞、等待鎖線程池耗盡、接口長(zhǎng)時(shí)間 hang 住語(yǔ)法jstack [-l] pid-l額外輸出鎖信息long listing排查死鎖必加?? 如果進(jìn)程卡死可以加-F強(qiáng)制 dumpjstack -F 12345但 - F 抓取信息會(huì)丟失部分信息優(yōu)先不用。?一個(gè)進(jìn)程里面包含多個(gè)線程。進(jìn)程是資源容器線程是 CPU 調(diào)度的最小單位。場(chǎng)景 1線上 CPU 飆升定位哪段代碼占用 CPU完整排查流程top找到j(luò)ava進(jìn)程PID12345top -H -p 12345查看進(jìn)程內(nèi)線程找到 CPU 最高線程號(hào)假設(shè)線程號(hào)23456十進(jìn)制jstack 12345 thread.log導(dǎo)出線程棧日志十進(jìn)制轉(zhuǎn)十六進(jìn)制printf %x\n 23456→ 0x5ba0nid在 thread.log 搜索nid0x5ba0找到對(duì)應(yīng)線程棧定位代碼行-p 12345只監(jiān)控 PID12345 這個(gè)進(jìn)程-H開(kāi)啟線程模式把進(jìn)程內(nèi)部所有線程展示出來(lái)top 默認(rèn)只展示進(jìn)程不展開(kāi)線程場(chǎng)景 2排查死鎖jstack -l 12345 deadlock.logjstack 會(huì)自動(dòng)檢測(cè)死鎖日志末尾會(huì)輸出Found one Java-level deadlock打印兩個(gè)線程互相等待的鎖和代碼位置。示例死鎖片段Found one Java-level deadlock: Thread-A: waiting to lock monitor 0x00007f8b1c003210 (object 0x000000076ac21498, a java.lang.Object), which is held by Thread-B Thread-B: waiting to lock monitor 0x00007f8b1c003320 (object 0x000000076ac214a8, a java.lang.Object), which is held by Thread-A場(chǎng)景 3接口響應(yīng)緩慢大量線程 WAITING/BLOCKEDjstack 12345 stack.log看線程狀態(tài)BLOCKED被鎖阻塞WAITING等待喚醒lock.wait () / 線程池隊(duì)列等待TIMED_WAITING限時(shí)等待sleepRUNNABLE運(yùn)行中IO 阻塞也會(huì)顯示 RUNNABLE常見(jiàn)坑大量線程卡在數(shù)據(jù)庫(kù)連接池等待就是大量 WAITING。三、jmap內(nèi)存映像工具堆內(nèi)存分析OOM、內(nèi)存泄漏全稱Java Memory MapJDK 自帶堆內(nèi)存分析工具。核心能力查看堆配置、查看當(dāng)前堆使用情況、dump 堆快照文件用于分析OOM、內(nèi)存泄漏。語(yǔ)法jmap [option] pid常用 optionjmap -heap pid打印堆配置與當(dāng)前堆概覽快速看堆參數(shù)jmap -histo[:live] pid查看堆內(nèi)對(duì)象統(tǒng)計(jì)對(duì)象數(shù)量、占用大小histo所有對(duì)象histo:live只統(tǒng)計(jì)存活對(duì)象會(huì)觸發(fā) FullGCjmap -dump:formatb,fileheap.hprof pid導(dǎo)出 hprof 堆快照文件核心?? dump 堆文件會(huì)暫停應(yīng)用生產(chǎn)盡量業(yè)務(wù)低峰操作hprof 文件建議下載本地用 MATMemory Analyzer Tool分析。場(chǎng)景 1快速查看 JVM 堆配置Xmx/Xms/ 新生代比例jmap -heap 12345輸出堆最大最小Eden、Survivor、Old 大小垃圾收集器類(lèi)型。場(chǎng)景 2查看堆中對(duì)象數(shù)量初步判斷內(nèi)存泄漏jmap -histo:live 12345 histo.log輸出按占用內(nèi)存排序num instances bytes class name 1 1200000 48000000 java.util.ArrayList 2 800000 32000000 com.xxx.entity.User如果自定義實(shí)體類(lèi)數(shù)量持續(xù)暴漲大概率內(nèi)存泄漏。場(chǎng)景 3OOM 發(fā)生導(dǎo)出堆快照最常用jmap -dump:formatb,fileheap_20261008.hprof 12345拿到heap_20261008.hprof文件下載到本地用 MAT 工具打開(kāi)看大對(duì)象看對(duì)象引用鏈找出是誰(shuí)持有對(duì)象沒(méi)有釋放推薦JVM 啟動(dòng)參數(shù)增加-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/xxx/OOM 時(shí)自動(dòng) dump 堆文件不用手動(dòng)執(zhí)行 jmap。不推薦場(chǎng)景生產(chǎn)不要頻繁執(zhí)行jmap -histo:live因?yàn)榧恿?live會(huì)觸發(fā) FullGC線上業(yè)務(wù)會(huì)卡頓。四、三個(gè)工具對(duì)比總結(jié)命令關(guān)注點(diǎn)適用場(chǎng)景jstatGC 指標(biāo)、類(lèi)加載、JIT持續(xù)監(jiān)控 GC判斷是否有 GC 異常jstack線程棧、鎖、線程狀態(tài)CPU 高、死鎖、接口 hang、線程阻塞jmap堆內(nèi)存、對(duì)象、堆 dumpOOM、內(nèi)存泄漏分析對(duì)象占用五、一套完整線上故障排查流程示例現(xiàn)象服務(wù)接口變慢時(shí)不時(shí) OOM老年代持續(xù)上漲jps -l獲取 pidjstat -gcutil pid 1000觀察發(fā)現(xiàn) Old 不斷上漲FGC 越來(lái)越多jstack pid查看是否有線程泄漏、連接不釋放jmap -histo pid看到自定義業(yè)務(wù)對(duì)象數(shù)量持續(xù)暴漲jmap -dump:formatb,fileheap.hprof pid導(dǎo)出堆文件MAT 分析 hprof找到對(duì)象引用鏈定位代碼泄漏點(diǎn)六、擴(kuò)展Q1jstat -gcutil 各個(gè)字段含義FGC 持續(xù)上漲代表什么答S0 S1 E O M CCS YGC YGCT FGC FGCT GCTS0Survivor0 使用率S1Survivor1 使用率EEden 區(qū)使用率O老年代使用率M元空間 Metaspace 使用率CCS壓縮類(lèi)空間使用率YGC年輕代 GC 次數(shù)YGCTYGC 總耗時(shí)FGCFullGC 次數(shù)FGCTFullGC 總耗時(shí)GCTGC 總耗時(shí)FGC 持續(xù)上漲分兩種情況每次 FGC 后 Old 能明顯下降正常業(yè)務(wù)短期大對(duì)象可觀察每次 FGC 后 Old 下降很少很快又漲滿內(nèi)存泄漏對(duì)象無(wú)法回收需要 jmap dump 堆分析。面試加分FGC 非常耗時(shí)間會(huì) STW頻繁 FGC 會(huì)造成接口超時(shí)、服務(wù)卡頓。Q2jstack 怎么排查 CPU100%完整步驟答top找到 Java 進(jìn)程 PIDtop -H -p pid查看進(jìn)程內(nèi)所有線程找到 CPU 最高的十進(jìn)制線程 idprintf %x\n 線程id轉(zhuǎn)為十六進(jìn)制 nidjstack pid thread.log導(dǎo)出線程棧在日志搜索nid0x十六進(jìn)制值定位到對(duì)應(yīng)線程棧找到阻塞 / 死循環(huán)代碼。注意jstack 輸出里的 nid 是十六進(jìn)制top 看到的線程號(hào)是十進(jìn)制很多人這里踩坑。Q3jstack 看到大量 BLOCKED、WAITING 分別是什么含義答B(yǎng)LOCKED線程正在等待監(jiān)視器鎖synchronized拿不到鎖被阻塞大概率鎖競(jìng)爭(zhēng)激烈、死鎖WAITING無(wú)限等待等待被其他線程喚醒如Object.wait()、LockSupport.park()常見(jiàn)線程池?zé)o任務(wù)、數(shù)據(jù)庫(kù)連接池拿不到連接TIMED_WAITING限時(shí)等待Thread.sleep()、wait(time)RUNNABLE運(yùn)行中注意網(wǎng)絡(luò) IO 阻塞時(shí)線程狀態(tài)依然是 RUNNABLE這點(diǎn)很容易答錯(cuò)。Q4jmap -histo 和 jmap -histo:live 的區(qū)別生產(chǎn)慎用哪個(gè)為什么答jmap -histo pid打印堆中所有對(duì)象統(tǒng)計(jì)不觸發(fā) FullGCjmap -histo:live pid只統(tǒng)計(jì)存活對(duì)象執(zhí)行前會(huì)觸發(fā)一次 FullGC。生產(chǎn)慎用histo:live因?yàn)?FullGC 會(huì) STW業(yè)務(wù)請(qǐng)求卡頓高峰期可能雪崩。Q5jmap dump 堆快照有什么風(fēng)險(xiǎn)生產(chǎn)什么時(shí)候 dump答風(fēng)險(xiǎn)dump 過(guò)程會(huì)STW暫停應(yīng)用hprof 文件大小接近堆最大值占用大量磁盤(pán) IO堆越大dump 耗時(shí)越長(zhǎng)。最佳實(shí)踐低峰期 dump優(yōu)先配置 JVM 參數(shù)-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/tmpOOM 時(shí)自動(dòng) dump不需要人工執(zhí)行 jmap拿到 hprof 下載到本地用 MAT 分析不要在線上直接分析。Q6jstat、jstack、jmap 三者分工故障排查時(shí)使用順序答jstat宏觀監(jiān)控看 GC 指標(biāo)先判斷有沒(méi)有內(nèi)存 / GC 問(wèn)題jstack線程層面查 CPU 高、死鎖、接口 hangjmap內(nèi)存對(duì)象層面OOM、內(nèi)存泄漏dump 堆快照。排查順序jps→jstat確認(rèn) GC 異?!鷍stack看線程是否阻塞→jmap導(dǎo)出堆定位泄漏對(duì)象Q7jstack -F 參數(shù)作用缺點(diǎn)答jstack pid正常抓取當(dāng)進(jìn)程卡死正常 jstack 無(wú)法響應(yīng)時(shí)使用jstack -F pid強(qiáng)制 dump。 缺點(diǎn)強(qiáng)制抓取會(huì)丟失部分信息丟失 Java 鎖信息優(yōu)先不用。Q8容器環(huán)境使用這些命令有什么坑答容器內(nèi) Java 進(jìn)程 PID 是容器內(nèi) PID宿主機(jī)看不到 解決啟動(dòng)容器添加--pidhost共享宿主機(jī) pid 命名空間 另外注意JDK 鏡像必須裝 JDK不能只用 JRE缺少 jstat/jstack/jmap 工具。Q9OOM 一定是內(nèi)存泄漏嗎答不一定。內(nèi)存泄漏對(duì)象不再使用但是一直被引用GC 無(wú)法回收Old 持續(xù)上漲非泄漏 OOM一次性加載超大文件 / 大查詢瞬間分配對(duì)象超過(guò)堆上限堆配置太小??梢酝ㄟ^(guò) jstat 觀察區(qū)分泄漏是緩慢持續(xù)漲老年代大對(duì)象是瞬間把 Old 打滿。Q10jstat 可以看線程堆棧嗎jstack 能看 GC 指標(biāo)嗎答都不行。 jstat只輸出統(tǒng)計(jì)指標(biāo)看不到代碼堆棧 jstack只打印線程棧不統(tǒng)計(jì) GC、內(nèi)存占用 各司其職。