您好,登錄后才能下訂單哦!
前言
對(duì)于后端程序員,特別是 Java 程序員來(lái)講,排查線上問(wèn)題是不可避免的。各種 CPU 飚高,內(nèi)存溢出,頻繁 GC 等等,這些都是令人頭疼的問(wèn)題。樓主同樣也遇到過(guò)這些問(wèn)題,那么,遇到這些問(wèn)題該如何解決呢?
首先,出現(xiàn)問(wèn)題,肯定要先定位問(wèn)題所在,然后分析問(wèn)題原因,再然后解決問(wèn)題,最后進(jìn)行總結(jié),防止下次再次出現(xiàn)。
今天的文章,就如我們的題目一樣,講的是基本操作,也就是一些排查線上問(wèn)題的基本方法。為什么這么說(shuō)呢?因?yàn)榫€上問(wèn)題千奇百怪,就算是身經(jīng)百戰(zhàn)的專家也會(huì)遇到棘手的問(wèn)題,因此不可能在一篇文章里說(shuō)完,還有一個(gè)最重要的原因,當(dāng)然就是樓主的水平不到位。
但不到位歸不到位,任何經(jīng)驗(yàn)都是值得記錄的,因此,樓主有必要將這些問(wèn)題記錄一下。
還有,本文的排查環(huán)境是 Linux.
CPU 飚高
線上 CPU 飚高問(wèn)題大家應(yīng)該都遇到過(guò),那么如何定位問(wèn)題呢?
思路:首先找到 CPU 飚高的那個(gè) Java 進(jìn)程,因?yàn)槟愕?a title="服務(wù)器" target="_blank" href="http://kemok4.com/">服務(wù)器會(huì)有多個(gè) JVM 進(jìn)程。然后找到那個(gè)進(jìn)程中的 “問(wèn)題線程”,最后根據(jù)線程堆棧信息找到問(wèn)題代碼。最后對(duì)代碼進(jìn)行排查。
如何操作呢?
通過(guò) top 命令找到 CPU 消耗最高的進(jìn)程,并記住進(jìn)程 ID。
再次通過(guò) top -Hp [進(jìn)程 ID] 找到 CPU 消耗最高的線程 ID,并記住線程 ID.
通過(guò) JDK 提供的 jstack 工具 dump 線程堆棧信息到指定文件中。具體命令:jstack -l [進(jìn)程 ID] >jstack.log。
由于剛剛的線程 ID 是十進(jìn)制的,而堆棧信息中的線程 ID 是16進(jìn)制的,因此我們需要將10進(jìn)制的轉(zhuǎn)換成16進(jìn)制的,并用這個(gè)線程 ID 在堆棧中查找。使用 printf "%x\n" [十進(jìn)制數(shù)字] ,可以將10進(jìn)制轉(zhuǎn)換成16進(jìn)制。
通過(guò)剛剛轉(zhuǎn)換的16進(jìn)制數(shù)字從堆棧信息里找到對(duì)應(yīng)的線程堆棧。就可以從該堆棧中看出端倪。
從樓主的經(jīng)驗(yàn)來(lái)看,一般是某個(gè)業(yè)務(wù)死循環(huán)沒(méi)有出口,這種情況可以根據(jù)業(yè)務(wù)進(jìn)行修復(fù)。還有 C2 編譯器執(zhí)行編譯時(shí)也會(huì)搶占 CPU,什么是 C2編譯器呢?當(dāng) Java 某一段代碼執(zhí)行次數(shù)超過(guò)10000次(默認(rèn))后,就會(huì)將該段代碼從解釋執(zhí)行改為編譯執(zhí)行,也就是編譯成機(jī)器碼以提高速度。而這個(gè) C2編譯器就是做這個(gè)的。如何解決呢?項(xiàng)目上線后,可以先通過(guò)壓測(cè)工具進(jìn)行預(yù)熱,這樣,等用戶真正訪問(wèn)的時(shí)候,C2編譯器就不會(huì)干擾應(yīng)用程序了。如果是 GC 線程導(dǎo)致的,那么極有可能是 Full GC ,那么就要進(jìn)行 GC 的優(yōu)化。
內(nèi)存問(wèn)題排查
說(shuō)完了 CPU 的問(wèn)題排查,再說(shuō)說(shuō)內(nèi)存的排查,通常,內(nèi)存的問(wèn)題就是 GC 的問(wèn)題,因?yàn)?Java 的內(nèi)存由 GC 管理。有2種情況,一種是內(nèi)存溢出了,一種是內(nèi)存沒(méi)有溢出,但 GC 不健康。
內(nèi)存溢出的情況可以通過(guò)加上 -XX:+HeapDumpOnOutOfMemoryError 參數(shù),該參數(shù)作用是:在程序內(nèi)存溢出時(shí)輸出 dump 文件。
有了 dump 文件,就可以通過(guò) dump 分析工具進(jìn)行分析了,比如常用的MAT,Jprofile,jvisualvm 等工具都可以分析,這些工具都能夠看出到底是哪里溢出,哪里創(chuàng)建了大量的對(duì)象等等信息。
第二種情況就比較復(fù)雜了。GC 的健康問(wèn)題。
通常一個(gè)健康的 GC 是什么狀態(tài)呢?根據(jù)樓主的經(jīng)驗(yàn),YGC 5秒一次左右,每次不超過(guò)50毫秒,F(xiàn)GC 最好沒(méi)有,CMS GC 一天一次左右。
而 GC 的優(yōu)化有2個(gè)維度,一是頻率,二是時(shí)長(zhǎng)。
我們看YGC,首先看頻率,如果 YGC 超過(guò)5秒一次,甚至更長(zhǎng),說(shuō)明系統(tǒng)內(nèi)存過(guò)大,應(yīng)該縮小容量,如果頻率很高,說(shuō)明 Eden 區(qū)過(guò)小,可以將 Eden 區(qū)增大,但整個(gè)新生代的容量應(yīng)該在堆的 30% - 40%之間,eden,from 和 to 的比例應(yīng)該在 8:1:1左右,這個(gè)比例可根據(jù)對(duì)象晉升的大小進(jìn)行調(diào)整。
如果 YGC 時(shí)間過(guò)長(zhǎng)呢?YGC 有2個(gè)過(guò)程,一個(gè)是掃描,一個(gè)是復(fù)制,通常掃描速度很快,復(fù)制速度相比而言要慢一些,如果每次都有大量對(duì)象要復(fù)制,就會(huì)將 STW 時(shí)間延長(zhǎng),還有一個(gè)情況就是 StringTable ,這個(gè)數(shù)據(jù)結(jié)構(gòu)中存儲(chǔ)著 String.intern 方法返回的常連池的引用,YGC 每次都會(huì)掃描這個(gè)數(shù)據(jù)結(jié)構(gòu)(HashTable),如果這個(gè)數(shù)據(jù)結(jié)構(gòu)很大,且沒(méi)有經(jīng)過(guò) FGC,那么也會(huì)拉長(zhǎng) STW 時(shí)長(zhǎng),還有一種情況就是操作系統(tǒng)的虛擬內(nèi)存,當(dāng) GC 時(shí)正巧操作系統(tǒng)正在交換內(nèi)存,也會(huì)拉長(zhǎng) STW 時(shí)長(zhǎng)。
再來(lái)看看FGC,實(shí)際上,F(xiàn)GC 我們只能優(yōu)化頻率,無(wú)法優(yōu)化時(shí)長(zhǎng),因?yàn)檫@個(gè)時(shí)長(zhǎng)無(wú)法控制。如何優(yōu)化頻率呢?
首先,F(xiàn)GC 的原因有幾個(gè),
1 是 Old 區(qū)內(nèi)存不夠,
2 是元數(shù)據(jù)區(qū)內(nèi)存不夠,
3 是 System.gc(),
4 是 jmap 或者 jcmd,
5 是CMS Promotion failed 或者 concurrent mode failure,
6 JVM 基于悲觀策略認(rèn)為這次 YGC 后 Old 區(qū)無(wú)法容納晉升的對(duì)象,因此取消 YGC,提前 FGC。
通常優(yōu)化的點(diǎn)是 Old 區(qū)內(nèi)存不夠?qū)е?FGC。如果 FGC 后還有大量對(duì)象,說(shuō)明 Old 區(qū)過(guò)小,應(yīng)該擴(kuò)大 Old 區(qū),如果 FGC 后效果很好,說(shuō)明 Old 區(qū)存在了大量短命的對(duì)象,優(yōu)化的點(diǎn)應(yīng)該是讓這些對(duì)象在新生代就被 YGC 掉,通常的做法是增大新生代,如果有大而短命的對(duì)象,通過(guò)參數(shù)設(shè)置對(duì)象的大小,不要讓這些對(duì)象進(jìn)入 Old 區(qū),還需要檢查晉升年齡是否過(guò)小。如果 YGC 后,有大量對(duì)象因?yàn)闊o(wú)法進(jìn)入 Survivor 區(qū)從而提前晉升,這時(shí)應(yīng)該增大 Survivor 區(qū),但不宜太大。
上面說(shuō)的都是優(yōu)化的思路,我們也需要一些工具知道 GC 的狀況。
JDK 提供了很多的工具,比如 jmap ,jcmd 等,oracle 官方推薦使用 jcmd 代替 jmap,因?yàn)?jcmd 確實(shí)能代替 jmap 很多功能。jmap 可以打印對(duì)象的分布信息,可以 dump 文件,注意,jmap 和 jcmd dump 文件的時(shí)候會(huì)觸發(fā) FGC ,使用的時(shí)候注意場(chǎng)景。
還有一個(gè)比較常用的工具是 jstat,該工具可以查看GC 的詳細(xì)信息,比如eden ,from,to,old 等區(qū)域的內(nèi)存使用情況。
還有一個(gè)工具是 jinfo,該工具可以查看當(dāng)前 jvm 使用了哪些參數(shù),并且也可以在不停機(jī)的情況下修改參數(shù)。
包括我們上面說(shuō)的一些分析 dump 文件的可視化工具,MAT,Jprofile,jvisualvm 等,這些工具可以分析 jmap dump 下來(lái)的文件,看看哪個(gè)對(duì)象使用的內(nèi)存較多,通常是能夠查出問(wèn)題的。
歡迎大家關(guān)注我的公種浩【程序員追風(fēng)】,文章都會(huì)在里面更新,整理的資料也會(huì)放在里面。
還有很重要的一點(diǎn)就是,線上環(huán)境一定要帶上 GC 日志?。?!
總結(jié)
基于文章的標(biāo)題,我們這個(gè)是基本操作,故障排查是說(shuō)不完的話題,每個(gè)故障涉及的知識(shí)也都很多,因此,我們?cè)趯W(xué)習(xí)了基本的排查之后,還需要學(xué)習(xí)更多事故排查技術(shù),比如排查 IO,網(wǎng)絡(luò),TCP 連接等等。樓主將在后面的文章中將這些基本操作都記錄下來(lái)。
最后
歡迎大家一起交流,喜歡文章記得點(diǎn)個(gè)贊喲,感謝支持!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。