手撕GC問題【壹】-壓測(cè)觀察并調(diào)試GC

GC問題

線上常出現(xiàn)的GC問題一般分為以下三種:

  1. GC 時(shí)間過長(zhǎng)返弹。這種會(huì)影響用戶的體驗(yàn)锈玉,這個(gè)時(shí)候就需要觀察日志,調(diào)整某些 JVM 參數(shù)义起。
  2. 發(fā)生了OOM拉背。這種情況就比較嚴(yán)重了,服務(wù)阻塞默终,我們要尋找背后的原因椅棺。
  3. 占用大量系統(tǒng)資源(主要探討堆外內(nèi)存溢出),服務(wù)進(jìn)程被killer齐蔽。這種情況就更嚴(yán)重了两疚,服務(wù)直接被動(dòng)停機(jī),連異常日志都不留下含滴。

下手解決思路

想要下手解決 GC 問題诱渤,我們以三個(gè)篇章來逐步討論。

  • 壓測(cè)觀察并調(diào)試GC-JVisualVM
  • 線上OOM異常處理-dump堆信息+MAT
  • 程序被動(dòng)停機(jī)(堆外內(nèi)存排查)

上線前壓測(cè)并觀察調(diào)試GC情況

我們可以想到的優(yōu)化手段蛙吏,包括代碼優(yōu)化源哩、擴(kuò)容、參數(shù)優(yōu)化鸦做,甚至我們的估算励烦,都需要一些支撐信息加以判斷。這時(shí)候就需要一些信息收集手段泼诱。

JVisualVM 工具使用

JDK自帶JVM監(jiān)控工具坛掠,可以實(shí)時(shí)觀察JVM進(jìn)程相關(guān)情況。但因?yàn)檫h(yuǎn)程連接JVM進(jìn)程的話治筒,需要遠(yuǎn)程進(jìn)程啟動(dòng)JMX服務(wù)屉栓,故不適合線上環(huán)境JVM觀察,比較適合測(cè)試環(huán)境搭配壓測(cè)調(diào)試使用耸袜。

JDK6之后就在JDK中自帶了友多,工具啟動(dòng)路徑 jdk*/bin/jvisualvm.exe

一般還需安裝一個(gè)插件Visual GC(官網(wǎng)下載 http://visualvm.github.io/ ---> Plugins)

visual gc 指數(shù)圖示


命令行指令

當(dāng)然直接使用JDK命令行指令也可做到JVM實(shí)時(shí)觀察。

  1. jps:查看正在運(yùn)行JVM相關(guān)進(jìn)程信息
  2. jstat:查看指定JVM的運(yùn)行狀態(tài)信息(類加載堤框,內(nèi)存域滥,垃圾回收纵柿,即時(shí)編譯)
  3. jinfo:查看/調(diào)整指定JVM的各項(xiàng)參數(shù)
  4. jmap:生成指定JVM堆轉(zhuǎn)儲(chǔ)快照(dump),及查詢堆與方法區(qū)的信息
  5. jstack:生成指定JVM棧(線程)快照

// TODO 后續(xù)補(bǔ)充


GC 日志輸出

你可能感受到启绰,最近幾年 Java 的版本更新速度是很快的昂儒,JVM 的參數(shù)配置其實(shí)變化也很大。就拿 GC 日志這一塊來說委可,Java 9 幾乎是推翻重來渊跋。網(wǎng)絡(luò)上的一些文章,把這些參數(shù)寫的亂七八糟着倾,根本不能投入生產(chǎn)拾酝。如果你碰到不能被識(shí)別的參數(shù),先確認(rèn)一下自己的 Java 版本屈呕。

在事故出現(xiàn)的時(shí)候微宝,通常并不是那么溫柔。你可能在半夜里就能接到報(bào)警電話虎眨,這是因?yàn)楹芏喽〞r(shí)任務(wù)都設(shè)定在夜深人靜的時(shí)候執(zhí)行蟋软。

這個(gè)時(shí)候,再去看 jstat 已經(jīng)來不及了嗽桩,我們需要保留現(xiàn)場(chǎng)岳守。這個(gè)便是看門狗的工作,看門狗可以通過設(shè)置一些 JVM 參數(shù)進(jìn)行配置碌冶。

那在實(shí)踐中湿痢,要怎么用呢?請(qǐng)看下面命令行扑庞。

Java 8

我們先看一下 JDK8 中的使用譬重。

#!/bin/sh
LOG_DIR="/tmp/logs"
JAVA_OPT_LOG=" -verbose:gc"
JAVA_OPT_LOG="${JAVA_OPT_LOG} -XX:+PrintGCDetails"
JAVA_OPT_LOG="${JAVA_OPT_LOG} -XX:+PrintGCDateStamps"
JAVA_OPT_LOG="${JAVA_OPT_LOG} -XX:+PrintGCApplicationStoppedTime"
JAVA_OPT_LOG="${JAVA_OPT_LOG} -XX:+PrintTenuringDistribution"
JAVA_OPT_LOG="${JAVA_OPT_LOG} -Xloggc:${LOG_DIR}/gc_%p.log"
JAVA_OPT_OOM=" -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_DIR} -XX:ErrorFile=${LOG_DIR}/hs_error_pid%p.log "
JAVA_OPT="${JAVA_OPT_LOG} ${JAVA_OPT_OOM}"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"

合成一行。

-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps 
-XX:+PrintGCApplicationStoppedTime -XX:+PrintTenuringDistribution 
-Xloggc:/tmp/logs/gc_%p.log -XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=/tmp/logs -XX:ErrorFile=/tmp/logs/hs_error_pid%p.log 
-XX:-OmitStackTraceInFastThrow

然后我們來解釋一下這些參數(shù):

參數(shù) 意義
-verbose:gc 打印 GC 日志
PrintGCDetails 打印詳細(xì) GC 日志
PrintGCDateStamps 系統(tǒng)時(shí)間罐氨,更加可讀臀规,PrintGCTimeStamps 是 JVM 啟動(dòng)時(shí)間
PrintGCApplicationStoppedTime 打印 STW 時(shí)間
PrintTenuringDistribution 打印對(duì)象年齡分布,對(duì)調(diào)優(yōu) MaxTenuringThreshold 參數(shù)幫助很大
loggc 將以上 GC 內(nèi)容輸出到文件中

OOM相關(guān)參數(shù):

參數(shù) 意義
HeapDumpOnOutOfMemoryError OOM 時(shí) Dump 信息栅隐,非常有用
HeapDumpPath Dump 文件保存路徑
ErrorFile 錯(cuò)誤日志存放路徑

注意到我們還設(shè)置了一個(gè)參數(shù) OmitStackTraceInFastThrow塔嬉,這是 JVM 用來縮簡(jiǎn)日志輸出的。

開啟這個(gè)參數(shù)之后租悄,如果你多次發(fā)生了空指針異常谨究,將會(huì)打印以下信息。

java.lang.NullPointerException
java.lang.NullPointerException
java.lang.NullPointerException
java.lang.NullPointerException

在實(shí)際生產(chǎn)中泣棋,這個(gè)參數(shù)是默認(rèn)開啟的胶哲,這樣就導(dǎo)致有時(shí)候排查問題非常不方便(很多研發(fā)對(duì)此無能為力),我們這里把它關(guān)閉潭辈,但這樣它會(huì)輸出所有的異常堆棧鸯屿,日志會(huì)多很多俩檬。

Java 13

再看下 JDK 13 中的使用。

從 Java 9 開始碾盟,移除了 40 多個(gè) GC 日志相關(guān)的參數(shù)。具體參見 JEP 158技竟。所以這部分的日志配置有很大的變化冰肴。

我們同樣看一下它的生成腳本。

#!/bin/sh
LOG_DIR="/tmp/logs"
JAVA_OPT_LOG=" -verbose:gc"
JAVA_OPT_LOG="${JAVA_OPT_LOG} -Xlog:gc,gc+ref=debug,gc+heap=debug,gc+age=trace:file=${LOG_DIR}/gc_%p.log:tags,uptime,time,level"
JAVA_OPT_LOG="${JAVA_OPT_LOG} -Xlog:safepoint:file=${LOG_DIR}/safepoint_%p.log:tags,uptime,time,level"
JAVA_OPT_OOM=" -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_DIR} -XX:ErrorFile=${LOG_DIR}/hs_error_pid%p.log "
JAVA_OPT="${JAVA_OPT_LOG} ${JAVA_OPT_OOM}"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
echo $JAVA_OPT

合成一行展示榔组。

-verbose:gc -Xlog:gc,gc+ref=debug,gc+heap=debug,gc+age=trace:file
=/tmp/logs/gc_%p.log:tags,uptime,time,level -Xlog:safepoint:file=/tmp
/logs/safepoint_%p.log:tags,uptime,time,level -XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=/tmp/logs -XX:ErrorFile=/tmp/logs/hs_error_pid%p.log 
-XX:-OmitStackTraceInFastThrow

可以看到 GC 日志的打印方式熙尉,已經(jīng)完全不一樣,但是比以前的日志參數(shù)規(guī)整了許多搓扯。

我們除了輸出 GC 日志检痰,還輸出了 safepoint 的日志。這個(gè)日志對(duì)我們分析問題也很重要锨推,那什么叫 safepoint 呢铅歼?

safepoint 是 JVM 中非常重要的一個(gè)概念,指的是可以安全地暫停線程的點(diǎn)换可。

當(dāng)發(fā)生 GC 時(shí)椎椰,用戶線程必須全部停下來,才可以進(jìn)行垃圾回收沾鳄,這個(gè)狀態(tài)我們可以認(rèn)為 JVM 是安全的(safe)慨飘,整個(gè)堆的狀態(tài)是穩(wěn)定的。

如果在 GC 前译荞,有線程遲遲進(jìn)入不了 safepoint瓤的,那么整個(gè) JVM 都在等待這個(gè)阻塞的線程,會(huì)造成了整體 GC 的時(shí)間變長(zhǎng)吞歼。

所以呢圈膏,并不是只有 GC 會(huì)掛起 JVM,進(jìn)入 safepoint 的過程也會(huì)浆熔。這個(gè)概念本辐,如果你有興趣可以自行深挖一下,一般是不會(huì)出問題的医增。

如果有人問起你在項(xiàng)目中都使用了哪些打印 GC 日志的參數(shù)慎皱,上面這些信息肯定是不很好記憶。你需要進(jìn)行以下總結(jié)叶骨。比如:

“我一般在項(xiàng)目中輸出詳細(xì)的 GC 日志茫多,并加上可讀性強(qiáng)的 GC 日志的時(shí)間戳。特別情況下我還會(huì)追加一些反映對(duì)象晉升情況和堆詳細(xì)信息的日志忽刽,用來排查問題天揖。另外夺欲,OOM 時(shí)自動(dòng) Dump 堆棧,我一般也會(huì)進(jìn)行配置今膊⌒┰模”


GC 日志的意義

我們首先看一段日志,然后簡(jiǎn)要看一下各個(gè)階段的意義斑唬。

  1. 表示 GC 發(fā)生的時(shí)間市埋,一般使用可讀的方式打印恕刘;
  2. 表示日志表明是 G1 的“轉(zhuǎn)移暫停: 混合模式”缤谎,停頓了約 223ms;
  3. 表明由 8 個(gè) Worker 線程并行執(zhí)行褐着,消耗了 214ms坷澡;
  4. 表示 Diff 越小越好,說明每個(gè)工作線程的速度都很均勻含蓉;
  5. 表示外部根區(qū)掃描频敛,外部根是堆外區(qū)。JNI 引用馅扣,JVM 系統(tǒng)目錄姻政,Classloaders 等;
  6. 表示更新 RSet 的時(shí)間信息岂嗓;
  7. 表示該任務(wù)主要是對(duì) CSet 中存活對(duì)象進(jìn)行轉(zhuǎn)移(復(fù)制)汁展;
  8. 表示花在 GC 之外的工作線程的時(shí)間;
  9. 表示并行階段的 GC 總時(shí)間厌殉;
  10. 表示其他清理活動(dòng)食绿;
  11. 表示收集結(jié)果統(tǒng)計(jì);
  12. 表示時(shí)間花費(fèi)統(tǒng)計(jì)公罕。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末器紧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子楼眷,更是在濱河造成了極大的恐慌铲汪,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罐柳,死亡現(xiàn)場(chǎng)離奇詭異掌腰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)张吉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門齿梁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事勺择〈茨希” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵省核,是天一觀的道長(zhǎng)稿辙。 經(jīng)常有香客問我,道長(zhǎng)气忠,這世上最難降的妖魔是什么邓深? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮笔刹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冬耿。我一直安慰自己舌菜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布亦镶。 她就那樣靜靜地躺著日月,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缤骨。 梳的紋絲不亂的頭發(fā)上爱咬,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音绊起,去河邊找鬼精拟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛虱歪,可吹牛的內(nèi)容都是我干的蜂绎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼笋鄙,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼师枣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起萧落,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤践美,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后找岖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陨倡,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年许布,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玫膀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡爹脾,死狀恐怖帖旨,靈堂內(nèi)的尸體忽然破棺而出箕昭,到底是詐尸還是另有隱情,我是刑警寧澤解阅,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布落竹,位于F島的核電站,受9級(jí)特大地震影響货抄,放射性物質(zhì)發(fā)生泄漏述召。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一蟹地、第九天 我趴在偏房一處隱蔽的房頂上張望积暖。 院中可真熱鬧,春花似錦怪与、人聲如沸夺刑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遍愿。三九已至,卻和暖如春耘斩,著一層夾襖步出監(jiān)牢的瞬間沼填,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工括授, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坞笙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓荚虚,卻偏偏與公主長(zhǎng)得像羞海,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子曲管,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 通過這篇文章你能知道的問題: 1.如何判斷對(duì)象是活著還是死去却邓? 2.在Java語言中,可作為GCRoots的對(duì)象有...
    beneke閱讀 1,352評(píng)論 0 1
  • Catalog 1 怎么解決OOM院水?/ 怎么排查OOM腊徙?/ JVM調(diào)優(yōu)1.1 JDK自帶工具1.2 阿里開源JVM...
    allen鍋閱讀 360評(píng)論 0 1
  • 概述 本文介紹GC基礎(chǔ)原理和理論,GC調(diào)優(yōu)方法思路和方法檬某,基于Hotspot jdk1.8撬腾,學(xué)習(xí)之后將了解如何對(duì)生...
    caison閱讀 1,433評(píng)論 1 2
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒恢恼。表情可以傳達(dá)很多信息民傻。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 125,019評(píng)論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者漓踢,不喜歡去冒險(xiǎn)牵署,但是人生放棄了冒險(xiǎn),也就放棄了無數(shù)的可能喧半。 ...
    yichen大刀閱讀 6,049評(píng)論 0 4