一 線上常見問題定位
常見問題 1:CPU 利用率高
CPU 使用率是衡量系統繁忙程度的重要指標,一般情況下單純的 CPU 高並沒有問題,它代表系統正在不斷的處理我們的任務,但是如果 CPU 過高,導致任務處理不過來,從而引起 load 高,這個是非常危險需要關注的。 CPU 使用率的安全值沒有一個標準值,取決於你的系統是計算密集型還是 IO 密集型,一般計算密集型應用 CPU 使用率偏高 load 偏低,IO 密集型相反。
問題原因及定位:
1 頻繁 FullGC/YongGC
- 查看 gc 日誌
- jstat -gcutil pid 查看內存使用和 gc 情況
2 代碼消耗,如死循環,md5 等內存態操作
1)arthas (已開源:https://github.com/alibaba/arthas)
-
thread -n 5
查看 CPU 使用率最高的前 5 個線程(包含堆棧,第二部分有詳解)
2)jstack 查找
-
ps -ef | grep java
找到 Java 進程 id
-
top -Hp pid
找到使用 CPU 最高的線程
-
printf ‘0x%x’ tid
線程 id 轉化 16 進制
-
jstack pid | grep tid
找到線程堆棧
ps:輸入“1”可查看每個 CPU 的情況,之前有團隊遇到單個 CPU 被中間件綁定導致 CPU 飈高的 case。
常見問題 2:load 高
load 指單位時間內活躍進程數,包含運行態(runnable 和 running)和不可中斷態( IO、內核態鎖)。關鍵字是運行態和不可中斷態,運行態可以聯想到 Java 線程的 6 種狀態,如下,線程 new 之後處於 NEW 狀態,執行 start 進入 runnable 等待 CPU 調度,因此如果 CPU 很忙會導致 runnable 進程數增加;不可中斷態主要包含網絡 IO、磁盤 IO 以及內核態的鎖,如 synchronized 等。
問題原因及定位:
1 CPU 利用率高,可運行態進程數多
- 排查方法見常見問題一
2 iowait,等待 IO
- vmstat 查看 blocked 進程狀況
-
jstack -l pid | grep BLOCKED
查看阻塞態線程堆棧
3 等待內核態鎖,如 synchronized
-
jstack -l pid | grep BLOCKED
查看阻塞態線程堆棧 -
profiler
dump 線程棧,分析線程持鎖情況
常見問題 3:持續 FullGC
在瞭解 FullGC 原因之前,先花一點時間回顧下 jvm 的內存相關知識:
內存模型
新 new 的對象放在 Eden 區,當 Eden 區滿之後進行一次 MinorGC,並將存活的對象放入 S0;
當下一次 Eden 區滿的時候,再次進行 MinorGC,並將存活的對象和 S0 的對象放入S1(S0 和 S1 始終有一個是空的);
依次循環直到 S0 或者 S1 快滿的時候將對象放入 old 區,依次,直到 old 區滿進行 FullGC。
jdk1.7 之前 Java 類信息、常量池、靜態變量存儲在 Perm 永久代,類的原數據和靜態變量在類加載的時候放入 Perm 區,類卸載的時候清理;在 1.8 中,MetaSpace 代替 Perm 區,使用本地內存,常量池和靜態變量放入堆區,一定程度上解決了在運行時生成或加載大量類造成的 FullGC,如反射、代理、groovy 等。
回收器
年輕代常用 ParNew,複製算法,多線程並行;
老年代常用 CMS,標記清除算法(會產生內存碎片),併發收集(收集過程中有用戶線程產生對象)。
關鍵常用參數
CMSInitiatingOccupancyFraction 表示老年代使用率達到多少時進行 FullGC;
UseCMSCompactAtFullCollection 表示在進行 FullGC 之後進行老年代內存整理,避免產生內存碎片。
問題原因及定位:
1 prommotion failed
從S區晉升的對象在老年代也放不下導致 FullGC(fgc 回收無效則拋 OOM)。
1)survivor 區太小,對象過早進入老年代。
-
jstat -gcutil pid 1000
觀察內存運行情況; -
jinfo pid
查看 SurvivorRatio 參數;
2)大對象分配,沒有足夠的內存。
- 日誌查找關鍵字 “allocating large”;
- profiler 查看內存概況大對象分佈;
3)old 區存在大量對象。
- 實例數量前十的類:
jmap -histo pid | sort -n -r -k 2 | head -10
- 實例容量前十的類:
jmap -histo pid | sort -n -r -k 3 | head -10
- dump 堆,profiler 分析對象佔用情況
2 concurrent mode failed
在 CMS GC 過程中業務線程將對象放入老年代(併發收集的特點)內存不足。詳細原因:
1)fgc 觸發比例過大,導致老年代佔用過多,併發收集時用戶線程持續產生對象導致達到觸發 FGC 比例。
- jinfo 查看 CMSInitiatingOccupancyFraction 參數,一般 70~80 即可
2)老年代存在內存碎片。
- jinfo 查看 UseCMSCompactAtFullCollection 參數,在 FullGC 後整理內存
常見問題 4:線程池滿
Java 線程池以有界隊列的線程池為例,當新任務提交時,如果運行的線程少於 corePoolSize,則創建新線程來處理請求。如果正在運行的線程數等於 corePoolSize 時,則新任務被添加到隊列中,直到隊列滿。當隊列滿了後,會繼續開闢新線程來處理任務,但不超過 maximumPoolSize。當任務隊列滿了並且已開闢了最大線程數,此時又來了新任務,ThreadPoolExecutor 會拒絕服務。
問題原因及定位:
1 下游 RT 高,超時時間不合理
- 業務監控
- sunfire
- eagleeye
2 數據庫慢 sql 或者數據庫死鎖
- 日誌關鍵字 “Deadlock found when trying to get lock”
- Jstack 或 zprofiler 查看阻塞態線程
3 Java 代碼死鎖
jstack –l pid | grep -i –E 'BLOCKED | deadlock'
- dump thread 通過 zprofiler 分析阻塞線程和持鎖情況
常見問題 5:NoSuchMethodException
問題原因及定位:
1 jar 包衝突
java 在裝載一個目錄下所有 jar 包時,它加載的順序完全取決於操作系統。
- mvn dependency:tree 分析報錯方法所在的 jar 包版本,留下新的
- arthas:sc -d ClassName
- XX:+TraceClassLoading
2 同類問題
- ClassNotFoundException
- NoClassDefFoundError
- ClassCastException
二 常用工具介紹
常用命令
1 tail
-
-f
跟蹤文件
2 grep
-
-i
忽略大小寫 -
-v
反轉查找 -
-E
擴展正則表達式 :grep -E 'pattern1|pattern2' filename
3 pgm
-
-b
開啟併發 -
-p
指定併發數 -
-A
開啟 askpass
4 awk
-
-F
指定分隔符:awk -F “|” '{print $1}‘ | sort -r | uniq -c
5 sed
- 時間段匹配:
sed '/2020-03-02 10:00:00/,/2020-03-02 11:00:00/p' filename
arthas
阿里巴巴開源 Java 診斷工具(開源地址:https://github.com/alibaba/arthas),基於 javaAgent 方式,使用 Instrumentation 方式修改字節碼方式進行 Java 應用診斷。
基礎功能介紹
- dashboard:系統實時數據面板, 可查看線程,內存,gc 等信息
- thread:jvm 線程堆棧信息,如查看最繁忙的前 n 線程
- getstatic:獲取靜態屬性值,如 getstatic className attrName 可用於查看線上開關真實值
- sc:查看 jvm 已加載類信息,可用於排查 jar 包衝突
- sm:查看 jvm 已加載類的方法信息
- jad:反編譯 jvm 加載類信息,排查代碼邏輯沒執行原因
-
watch:觀測方法執行數據,包含出入參,異常等;
watch xxxClass xxxMethod " {params, throwExp} " -e -x 2
watch xxxClass xxxMethod "{params,returnObj}" "params[0].sellerId.equals('189')" -x 2
watch xxxClass xxxMethod sendMsg '@com.taobao.eagleeye.EagleEye@getTraceId()'
- trace:方法內部調用時長,並輸出每個節點的耗時,用於性能分析
- tt:用於記錄方法,並做回放
三 常見問題恢復
1 線程池滿
-
rpc 框架線程池滿
- 高 RT 接口進行線程數限流
-
應用內線程池滿
- 重啟可短暫緩解,具體還得看問題原因
2 CPU 高,load 高
- 單機置換或重啟,可短暫緩解,恢復看具體原因
- 集群高且流量大幅增加,擴容,恢復看具體原因
3 下游 RT 高
- 限流
- 降級
4 數據庫
-
死鎖
- kill 進程
-
慢 sql
- sql 限流
線上問題的排查是一個積累的過程,只有瞭解問題背後的原理才能更快速的定位和恢復,除此之外更需要有一些趁手的工具來輔助排查,從而降低整個團隊問題定位和快恢的門檻。