# 前言
對(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)的專(zhuān)家也會(huì)遇到棘手的問(wèn)題,因此不可能在一篇文章里說(shuō)完递雀,還有一個(gè)最重要的原因柄延,當(dāng)然就是樓主的水平不到位。
但不到位歸不到位映之,任何經(jīng)驗(yàn)都是值得記錄的拦焚,因此,樓主有必要將這些問(wèn)題記錄一下杠输。
還有赎败,本文的排查環(huán)境是 Linux.
#1. CPU 飚高
線上 CPU 飚高問(wèn)題大家應(yīng)該都遇到過(guò),那么如何定位問(wèn)題呢蠢甲?
思路:首先找到 CPU 飚高的那個(gè) Java 進(jìn)程僵刮,因?yàn)槟愕姆?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)化鲫竞。
2. 內(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)題的沈条。
還有很重要的一點(diǎn)就是,線上環(huán)境一定要帶上 GC 日志W缏@酢屋厘!
總結(jié)
基于文章的標(biāo)題,我們這個(gè)是基本操作月而,故障排查是說(shuō)不完的話題汗洒,每個(gè)故障涉及的知識(shí)也都很多,因此景鼠,我們?cè)趯W(xué)習(xí)了基本的排查之后仲翎,還需要學(xué)習(xí)更多事故排查技術(shù),比如排查 IO铛漓,網(wǎng)絡(luò)溯香,TCP 連接等等。樓主將在后面的文章中將這些基本操作都記錄下來(lái)浓恶。
good luckC堤场!0湿镀!