一次 Young GC 的優(yōu)化實踐(FinalReference 相關(guān))

一次 Young GC 的優(yōu)化實踐(FinalReference 相關(guān))

簡書 滌生
轉(zhuǎn)載請注明原創(chuàng)出處,謝謝!
如果讀完覺得有收獲的話,歡迎點贊加關(guān)注。

前言

博客已經(jīng)好久沒有更新了,主要原因是 18 年下半年工作比較忙,另外也沒有比較有意思的題材,所以遲遲沒有更新。
此篇是 18 年底的微信上的某同學(xué)提供的一個 Young GC 問題案例,找我?guī)兔鉀Q。這個 GC 案例比較有意思,雖然過去有一段時間了,但是想想覺得還是有必要寫出來,應(yīng)該對大家很有幫助。
排查問題有點像偵探斷案,先分析各種可能性,再按照獲得的一個個證據(jù),去排除各種可能性、然后定位原因,最終解決問題。

問題

有個同學(xué)在微信上問我,有沒有辦法排查 YoungGC 效率低的問題?聽到這話,我也是不知從何說起,就讓他說下具體情況。
具體情況是:
有個服務(wù)在沒有 RPC 調(diào)用時,YoungGC 時間大約在 4-5ms,但是有 RPC 調(diào)用時,YoungGC 的耗時在 40ms 以上,幾乎沒有什么對象晉升,頻率 4-5 秒一次。GC 日志截圖如下。


40+ms 耗時的 YoungGC 日志

后來他為了排查問題,把服務(wù)只留一個 RPC 調(diào)用,結(jié)果 YoungGC 更嚴重,變成 100ms 以上,幾乎沒有什么對象晉升,另外 RPC 調(diào)用耗時在 4-5ms,壓測的 QPS 也比較低,只有幾個線程在壓。GC 日志截圖如下。


100+ms 耗時的 YoungGC 日志

另外還有一個奇葩的現(xiàn)象,如果測試時,只留一個調(diào)用耗時更長的 RPC 進行測試,發(fā)現(xiàn) Young GC 耗時會小一點。
這里也提供下提供了下 GC 參數(shù)如下:

//GC 參數(shù)
-Xmn700m -Xms3072m -Xmx3072m -XX:SurvivorRatio=8 
-XX:MetaspaceSize=384m -XX:MaxMetaspaceSize=384m -XX:+UseConcMarkSweepGC 
-XX:+CMSScavengeBeforeRemark -XX:CMSInitiatingOccupancyFraction=80
-XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintGC -XX:+PrintGCDateStamps 
-XX:+PrintGCDetails

可以看到,整個堆 3072M,Young Gen只有 700M,都不大。

疑惑

從上述問題來看可以判斷出:RPC 調(diào)用影響了 YoungGC 的時間。
但是你一定有很多疑惑:

  • 為什么進行 RPC 調(diào)用和不進行 RPC 調(diào)用相比 YoungGC 耗時增加那么多?(Young Gen 的空間一直那么大,而且每次 GC 幾乎沒有對象晉升到 Old Gen,)
  • 為什么 RPC 調(diào)用耗時長短也會影響 YoungGC 的耗時?

分析

首先,大家都知道 Young GC 是全程 stop the world 的,時間可能有多方面原因決定:

  • 各個線程到達安全點的等待時間;
  • 從 GC Root 掃描對象,進行標(biāo)記的時間;
  • 存活對象 Copy 到 Survivor 以及晉升 Old Gen 到的時間;
  • GC 日志的時間;

原因比較多,從表象上很難看出 YoungGC 耗時的原因,因此,我們需要收集更多的證據(jù),來排除干擾選項,定位原因

  • 對于是否線程到達安全點時間引起的原因,
    我們加上顯示 Stop 時間與 Safepoint 的相關(guān)參數(shù)
//Stop時間與Safepoint的相關(guān)參數(shù)
-XX:+PrintGCApplicationStoppedTime -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1

結(jié)論也很明顯,stopping threads took 的時間都很短,可以排除此項因素。


stopping threads took 耗時
  • 對于從 GC Root 掃描對象,進行標(biāo)記的時間引起的原因
    我們加上顯示 GC 處理 Reference 耗時的相關(guān)參數(shù)
// 打印參數(shù)
-XX:+PrintReferenceGC

結(jié)論也很明顯,YoungGC 總耗時 110ms, 而 reference 處理耗時較長,主要是 FinalReference,耗時有 86 ms。

//YoungGC 日志
2019-01-02T17:42:53.926+0800: 409.638: [GC (Allocation Failure)
2019-01-02T17:42:53.927+0800: 409.638: [ParNew2019-01-02T17:42:53.950+0800: 409.662: [SoftReference, 0 refs, 0.0000893 secs]
2019-01-02T17:42:53.951+0800: 409.662: [WeakReference, 185 refs, 0.0000499 secs]
2019-01-02T17:42:53.951+0800: 409.662: [FinalReference, 38820 refs, 0.0865010 secs]
2019-01-02T17:42:54.037+0800: 409.749: [PhantomReference, 0 refs, 1 refs, 0.0000447 secs]
2019-01-02T17:42:54.037+0800: 409.749: [JNI Weak Reference, 0.0000220 secs]: 645120K->37540K(645120K), 0.1126527 secs]
1005305K->397726K(3074048K), 0.1128549 secs]
[Times: user=0.40 sys=0.00, real=0.11 secs]
  • 對于存活對象 Copy 到 Survivor 以及晉升 Old Gen 到的時間引起的原因
    由于 Survivor 較小,每次 YoungGC 又幾乎沒有晉升到 Old Gen 的對象,因此很明顯,可以排除此項因素。

  • 對 GC 日志的時間;
    大部分 GC 日志是不耗時的,除非機器使用了大量的 swap 空間,或者其他原因?qū)е碌?iowait 較高,此項可以通過 top 或者 dstat 等命令看看 swap 使用情況以及 iowait 指標(biāo)。

分析到這里,其實問題基本已經(jīng)定位了,主要是 FinalReference 的處理時間比較長,導(dǎo)致 Young GC 時間比較長。

原理

FinalReference 是什么?

FinalReference 的詳細講解,又需要一篇文章。
這里簡單描述下:
對于重載了 Object 類的 finalize 方法的類實例化的對象(這里稱為 f 對象),JVM 為了能在 GC 對象時觸發(fā) f 對象的 finalize 方法的調(diào)用,將每個 f 對象包裝生成一個對應(yīng)的FinalReference 對象,方便 GC 時進行處理。

//finalize方法
protected void finalize() throws Throwable {
    .... 
}

FinalReference 詳細解讀,可以看下你假笨大神的這篇博客JVM源碼分析之FinalReference完全解讀

FinalReference 來源何處?

FinalReference 對于沒有實現(xiàn) finalize 的程序,一般是不會出現(xiàn)的,到底是來源何處呢?
這里進行 JVM dump,然后通過 MAT 工具分析


JVM dump 的 Finalizer 相關(guān)對象

很明顯,是 SocksSocketImpl 對象,我們看下 SocksSocketImpl 類實現(xiàn)

//SocksSocketImpl finalize 的實現(xiàn)
/**
 * Cleans up if the user forgets to close it.
 */
protected void finalize() throws IOException {
      close();
}

這里是為了防止 Socket 連接忘記關(guān)閉導(dǎo)致資源泄漏而進行的保底措施。

為什么FinalReference GC 處理這么耗時?

為什么 JVM GC 處理 FinalReference 這么耗時呢,通過 GC 日志,可以看出有 38820 個 reference,耗時 86ms。

2019-01-02T17:42:53.951+0800: 409.662: [FinalReference, 38820 refs, 0.0865010 secs]

對于這個問題擼過 JVM 源碼,但是一直沒有搞清楚,
其實我的另一篇博客 PhantomReference導(dǎo)致CMS GC耗時嚴重,也是類似,reference 個數(shù)不多,但是 GC 處理非常耗時,影響系統(tǒng)性能呢。

如何解釋問題的想象?

看到上面的 FinalReference 主要是 Socket 引起的,當(dāng)時就推想到為什么會有這么多 Socket 對象需要 GC,所以問某同學(xué)難道你使用的是短連接?得到的回答是肯定的,瞬間豁然開朗。
上文提到的兩個疑惑就很容易解釋了:

  • 對于“為什么進行 RPC 調(diào)用和不進行 RPC 調(diào)用相比 YoungGC 耗時增加那么多?”問題
    RPC 調(diào)用使用的是短連接,每調(diào)用一次就會創(chuàng)建一個 Socket 對象,致使 FinalReference 對象非常多, 因此,YoungGC 耗時增加。

  • 對于“為什么 RPC 調(diào)用耗時長短也會影響 YoungGC 的耗時?”問題
    由于 RPC 調(diào)用耗時長的,同樣的線程數(shù),調(diào)用的 QPS 就低,QPS 低自然創(chuàng)建的 Socket 對象就少,致使 FinalReference 對象少,因此,YoungGC 耗時相比就會小一些。

解決

理解了問題產(chǎn)生的原理,解決問題自然變得非常簡單。

  • 通用方法
    加上 ParallelRefProcEnabled 參數(shù)可以使得 Reference 在 GC 的時候多線程并行處理過程,自然耗時就會降下來。
//ParallelRefProcEnabled 參數(shù)
-XX:+ParallelRefProcEnabled
  • 減少 GC 的 Reference 數(shù)量
    減少 GC 的 Reference 方法比較多,不同案例不同處理,能減少 GC 的 Reference 數(shù)量就好。
    這里也很簡單,RPC 調(diào)用短連接改用長鏈接,自然就能減少 GC 的 Reference 數(shù)量。
    該案例就使用了這個方案,效果也很明顯,YoungGC 時間直接降低到了 14ms。

總結(jié)

本案例總結(jié)原因就是 RPC 使用短連接調(diào)用,導(dǎo)致 Socket 的 FinalReference 引用較多,致使 YoungGC 耗時較長。因此,通過將短連接改成長連接,減少了 Socket 對象的創(chuàng)建,從而減少 FinalReference,來降低 YoungGC 耗時。
在看本篇文章之前,你一定不會想到 JVM GC 處理 FinalReference 耗時這么長;你也一定不會想到短連接還有影響 GC 耗時的壞處。
排查問題的過程,很享受,不僅可以證明所學(xué),也可以錘煉技術(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,750評論 2 370

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