首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Tomcat + Java Web 應用 CPU 100% 故障排查實戰:top → ps → jstack 定位死循環代碼

     編輯:Java 綜合教學

Tomcat + Java Web 應用 CPU 100% 故障排查實戰:top → ps → jstack 定位死循環代碼

生產環境 Tomcat 進程 CPU 長期佔用 100%,導致服務無響應。標準排查鏈路:top 定進程 → ps 定線程 → printf 轉十六進制 → jstack 定代碼行。本文記錄完整操作步驟、截圖解讀與代碼級定位技巧。

現象描述

  • Tomcat 運行一段時間後,Java 進程 CPU 持續 100%+。
  • Web 請求極慢或超時,重啟暫時緩解,隨後復發。
  • 疑似:業務邏輯死循環、頻繁 GC、正則回溯、鎖競爭。

排查四步法

步驟 1:top 定位高 CPU 進程 PID

top
# 按 P 排序 CPU,記下 PID(假設 28802)

top 輸出示例

步驟 2:ps 確認進程類型並列出線程 CPU 佔用

# 確認是 Tomcat 啟動的 Java 進程
ps -ef | grep 28802

# 列出該進程所有線程的 CPU 時間、TID
ps -mp 28802 -o THREAD,tid,time

ps 線程輸出

關鍵列:TID(線程 ID,十進制)、TIME(佔用 CPU 時間)。找到 TIME 最大的 TID(如 28802,已運行近 2 小時)。

步驟 3:printf 將十進制 TID 轉十六進制

printf "%x\n" 28802
# 輸出:7082  (十六進制)

printf 輸出

原因:jstack 輸出的線程 ID(nid)為十六進制。

步驟 4:jstack 導出堆棧並 grep 定位代碼

jstack 28802 | grep -A 30 7082
# -A 30 顯示匹配行後 30 行(堆棧通常 20-50 行)

jstack 輸出

結果分析與代碼定位

"Thread-123" #45 prio=5 os_prio=0 tid=0x00007f8b1c001000 nid=0x7082 runnable [0x00007f8b1d123000]
   java.lang.Thread.State: RUNNABLE
        at com.example.service.CalcService.calculate(CalcService.java:47)
        at com.example.controller.CalcController.calc(CalcController.java:22)
        ...
  • nid=0x7082 匹配目標線程。
  • RUNNABLE 狀態且無鎖等待 → 正在執行代碼(非 BLOCKED/WAITING)。
  • 堆棧指向 CalcService.java:47,打開源碼定位:
// CalcService.java 第 47 行附近
while (true) {           // 典型死循環
    complexCalculation(); // 耗時運算
}

常見高 CPU 代碼模式

模式特徵排查重點
死循環 while(true)/for(;;)RUNNABLE、堆棧固定在循環內缺少退出條件、條件永不滿足
正則回溯災難RUNNABLE、堆棧在 Pattern.match/Matcher.find惡意輸入觸發指數級回溯,改用非貪婪/原子組
頻繁 GC多線程 GC task thread、CPU 高但業務線程正常堆太小、內存洩漏、大對象頻繁創建
鎖競爭/自旋BLOCKED 或 RUNNABLE 在 synchronized/LockSupport.park鎖粒度太大、線程數過多、考慮無鎖數據結構
日志輸出阻塞RUNNABLE 在 FileOutputStream.write/System.out.println同步日志、磁盤慢、改異步日志框架

進階工具:Arthas / async-profiler(無需重啟)

# Arthas 線程儀表板,秒級定位熱點方法
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
# 選擇進程 → thread -n 10 顯示前 10 高 CPU 線程堆棧

# async-profiler 生成火焰圖
./profiler.sh -d 30 -f profile.html -e cpu 28802
# 瀏覽器打開 profile.html,直觀定位熱點代碼路徑

預防與最佳實踐

  1. 代碼審查:禁止無界 while(true),必須有退出條件或 Thread.sleep 降頻。
  2. 輸入驗證:正則表達式避免嵌套量詞 (a+)+,用 PossessiveQuantifier 或限制長度。
  3. 監控告警:Prometheus + Grafana 監控 process_cpu_seconds_total,閾值告警。
  4. JVM 參數:-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log 便於 GC 分析。
  5. 線程命名:ThreadFactoryBuilder.setNameFormat("biz-%d"),堆棧可讀性大增。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved