JVM完全解讀之GC日志記錄分析
相信大家在系統(tǒng)學(xué)習(xí)jvm的時(shí)候都會(huì)有遇到過這樣的問題,散落的jvm知識(shí)點(diǎn)知道很多,但是真正在線上環(huán)境遇到一些莫名其妙的gc異常時(shí)候卻無從下手去分析。
關(guān)于這塊的苦我也表示能夠理解,之前光是JVM相關(guān)的八股文就整理了許多,但是經(jīng)常是不知道如何在實(shí)戰(zhàn)中使用。最近也嘗試在模擬一些案例來訓(xùn)練自己的JVM相關(guān)知識(shí),本文特意記錄下這段調(diào)優(yōu)經(jīng)歷。
Java應(yīng)用的GC評(píng)估
可能大多數(shù)程序員在開發(fā)完某個(gè)需求之后,往線上環(huán)境一丟,然后就基本不怎么關(guān)注后續(xù)的變化了。但是是否有考慮過,這些新引入的代碼會(huì)對(duì)原有系統(tǒng)造成的影響呢?下邊我們通過一段實(shí)戰(zhàn)來帶各位讀者較好地去深入理解這個(gè)過程。
模擬場景
有一個(gè)應(yīng)用程序(暫且稱呼為moment服務(wù))準(zhǔn)備在小程序上開展社交動(dòng)態(tài)推送功能,大概就是每次用戶刷新頁面時(shí)候便會(huì)按照一定規(guī)則推送出20條用戶動(dòng)態(tài)數(shù)據(jù)。由于該產(chǎn)品的c端用戶數(shù)量比較多,于是便在上線該產(chǎn)品之前進(jìn)行了相應(yīng)的壓測,判斷該功能的承載能力。
在壓測開始的初期,接口響應(yīng)速度都還可以,但是漸漸地開始加壓的時(shí)候,發(fā)現(xiàn)程序出現(xiàn)了OOM。經(jīng)過排查后,排除數(shù)據(jù)庫層的問題。于是開始懷疑是否是Java應(yīng)用內(nèi)部出現(xiàn)了異常。
應(yīng)用的啟動(dòng)參數(shù):
java -Xmx1512m -Xms1512m -Xmn1024m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:log/gc.log -jar qiyu-framework-demo-jvm.jar
單節(jié)點(diǎn)壓測,壓力測試10w次請(qǐng)求,1000并發(fā)。使用ab工具進(jìn)行壓力測試:
ab -n100000 -c1000 http://localhost:8080/user/batch-query
jstat 查看GC,每隔5秒打印一次,持續(xù)20次
jstat -gc 5673 5000 20
經(jīng)過一段時(shí)間的施壓,在施壓的持續(xù)了1分鐘之后,YGC的頻率讓人感覺著實(shí)有些高。通常一個(gè)健康的系統(tǒng)ygc應(yīng)該是20-30min左右一次,full gc可能是好幾周才一次。
通過jstat可以看出,年輕代的gc會(huì)比較頻繁,并且停頓時(shí)間嚴(yán)重影響了正常的業(yè)務(wù)使用。為了得到更加精準(zhǔn)的數(shù)據(jù),我嘗試將gc日志放到GCeasy工具上進(jìn)行可視化分析:
這是一款非常不錯(cuò)的gc日志分析工具
https://www.gceasy.io/
GC日志的可視化分析
首先是JVM內(nèi)存中的占用分析,很清晰地可以看出,年輕代的內(nèi)存和老年代的內(nèi)存幾乎占滿,元空間基本沒有變動(dòng)過。
然后是整個(gè)系統(tǒng)的GC耗時(shí)分析:
從整體來看,大部分的GC耗時(shí)都是在0-100ms內(nèi),極端情況下的GC耗時(shí)可能會(huì)達(dá)到700ms。
接下來是看看GC回收對(duì)堆內(nèi)存整體的一個(gè)影響。觀測發(fā)現(xiàn),基本每次GC都能夠回收達(dá)改200mb左右的內(nèi)存。
再繼續(xù)分析,可以發(fā)現(xiàn)CMS回收器在回收的各個(gè)階段中所消耗的時(shí)間:初始標(biāo)記,并發(fā)標(biāo)記,修正標(biāo)記,并發(fā)清除
除了單純分析GC回收的耗時(shí)之外,這款工具還有個(gè)非常贊的功能,可以幫助我們分析這段時(shí)間內(nèi),該Java程序產(chǎn)生對(duì)象的速率:
可以發(fā)現(xiàn),一秒大概要產(chǎn)生445mb的對(duì)象,大概一秒就會(huì)有2.5mb對(duì)象晉升到老年代。
內(nèi)存逃逸分析沒有發(fā)現(xiàn)異常記錄。
通過對(duì)這份報(bào)告分析完畢之后,我的第一直覺告訴我,年輕代不足,需要對(duì)年輕代內(nèi)存進(jìn)行增加。但是仔細(xì)觀察下,產(chǎn)生對(duì)象的速率竟然高達(dá)445mb/s,這感覺非常不正常啊,極度懷疑是程序內(nèi)部存在大對(duì)象的情況。
于是嘗試使用jvisualVM這款工具進(jìn)行深入分析,通過對(duì)CPU樣例的監(jiān)控,發(fā)現(xiàn)了一些異常信息:
似乎這個(gè)方法對(duì)CPU的消耗比較高,接著是內(nèi)存的一個(gè)監(jiān)控:
此時(shí)大概可以定位出異常方法所在的位置了,接下來便是對(duì)系統(tǒng)內(nèi)部的業(yè)務(wù)代碼進(jìn)行分析了。
最后排查結(jié)果發(fā)現(xiàn),其實(shí)是系統(tǒng)內(nèi)部的一個(gè)方法調(diào)用,加載了5k個(gè)User對(duì)象到內(nèi)存中做計(jì)算,而每個(gè)User對(duì)象里存放了一個(gè)大小為1kb的byte數(shù)組。大概的代碼邏輯為:
于是便需要從業(yè)務(wù)層面對(duì)該方法進(jìn)行優(yōu)化,例如調(diào)小5k這個(gè)數(shù)值,同時(shí)對(duì)User對(duì)象內(nèi)的byte數(shù)組進(jìn)行過濾(因?yàn)閷?shí)際使用不到這個(gè)字段)。
調(diào)整后發(fā)現(xiàn)GC的頻率降低了許多,比較正常。
頻繁GC的排查思路總結(jié)
通過本次實(shí)驗(yàn),大概能夠梳理出Java應(yīng)用在出現(xiàn)頻繁GC的時(shí)候該如何去排查問題點(diǎn),大致為:
通過結(jié)合工具分析GC日志,排查是否有大量對(duì)象頻繁創(chuàng)建所導(dǎo)致。
通過對(duì)GC日志的分析能夠排查出年輕代和老年代的GC頻率。
通過對(duì)CPU占用比較高的線程,或者內(nèi)存占用比較高的對(duì)象進(jìn)行分析,定位異常點(diǎn)。
最后結(jié)合業(yè)務(wù)系統(tǒng)代碼進(jìn)行分析,精確定位異常點(diǎn)。
以上就是JVM完全解讀之GC日志記錄分析的詳細(xì)內(nèi)容,更多關(guān)于JVM解讀GC日志記錄分析的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaWeb入門教程之分頁查詢功能的簡單實(shí)現(xiàn)
這篇文章主要介紹了JavaWeb入門教程之分頁查詢功能的簡單實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11spring-boot react如何一步一步實(shí)現(xiàn)增刪改查
這篇文章主要介紹了spring-boot react如何一步一步實(shí)現(xiàn)增刪改查,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11Springboot @Configuration與自動(dòng)配置詳解
這篇文章主要介紹了SpringBoot中的@Configuration自動(dòng)配置,在進(jìn)行項(xiàng)目編寫前,我們還需要知道一個(gè)東西,就是SpringBoot對(duì)我們的SpringMVC還做了哪些配置,包括如何擴(kuò)展,如何定制,只有把這些都搞清楚了,我們?cè)谥笫褂貌艜?huì)更加得心應(yīng)手2022-07-07Lombok 的@StandardException注解解析
@StandardException 是一個(gè)實(shí)驗(yàn)性的注解,添加到 Project Lombok 的 v__1.18.22 版本中,在本教程中,我們將使用 Lombok 的 @StandardException 注解自動(dòng)生成異常類型類的構(gòu)造函數(shù),需要的朋友可以參考下2023-05-05spring batch使用reader讀數(shù)據(jù)的內(nèi)存容量問題詳解
這篇文章主要介紹了spring batch使用reader讀數(shù)據(jù)的內(nèi)存容量問題詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07java實(shí)現(xiàn)的漢字轉(zhuǎn)五筆功能實(shí)例
這篇文章主要介紹了java實(shí)現(xiàn)的漢字轉(zhuǎn)五筆功能,結(jié)合具體實(shí)例形式分析了java基于字符串遍歷與編碼轉(zhuǎn)換等操作實(shí)現(xiàn)五筆編碼獲取的相關(guān)操作技巧,需要的朋友可以參考下2017-06-06