問題現象
首先,我們一起看看通過 VisualVM 監控到的機器 CPU 使用率圖:
如上圖所示,在 下午3:45 分之前,CPU 的使用率明顯飆高,最高飆到近 100%,爲什麼會出現這樣的現象呢?
排查過程
Step 1:使用top
命令,查詢資源佔用情況:
如上圖所示,顯示了服務器當前的資源佔用情況,其中PID
爲5456
的進程佔用的資源最多。
在這裏,我們也使用top -p PID
命令,查詢指定PID
的資源佔用情況:
Step 2:使用ps -mp PID -o THREAD,tid,time
命令,查詢該進程的線程情況:
在這裏,我們也使用ps -mp PID -o THREAD,tid,time | sort -rn
命令,將該進程下的線程按資源使用情況倒序展示:
Step 3:使用printf "%x\n" PID
命令,將PID
轉爲十六進制的TID
:
在這裏,我們之所以需要將PID
轉爲十六進制是因爲在堆棧信息中,PID
是以十六進制形式存在的。
Step 4:使用jstack PID | grep TID -A 100
命令,查詢堆棧信息:
如上圖所示,顯示該進程下多個線程均處於TIMED_WAITING
狀態。
雖然線程處於WAITING
或者TIMED_WAITING
狀態都不會消耗 CPU,但是線程頻繁的掛起和喚醒卻會消耗 CPU,而且代價高昂。
而上面之所以會出現 CPU 使用率飆高的情況,則是因爲有人在做壓測。
特別地,在 mock 底層接口的時候,使用了類似TimeUnit.SECONDS.sleep(1)
這樣的語句。
至於爲何在 下午3:45 分之後,CPU 的使用率降下來了,則是因爲停止了壓測。
除此之外,我們還可以使用jinfo
和jstat
命令來查詢 Java 進程的啓動參數以及 GC 情況:
- 使用
jinfo PID
命令,查詢啓動參數:
如上圖所示,使用該命令我們主要是爲了查詢啓動參數,如初始化堆大小、垃圾回收器等配置。
- 使用
jstat -gcutil PID 1000
命令,查詢 GC 情況:
如上圖所示,顯示了PID
爲20567
的 Java 進程每秒的 GC 情況,其中1000
表示 GC 狀態的更新頻率,單位爲毫秒。