《JVM G1源碼分析和調(diào)優(yōu)》
Java核心技術(shù)系列

JVM G1源碼分析和調(diào)優(yōu)
彭成寒 編著
Preface 前 言
G1是目前最成熟的垃圾回收器,已經(jīng)廣泛應(yīng)用在眾多公司的生產(chǎn)環(huán)境中。我們知道,CMS作為使用最為廣泛的垃圾回收器,也有令人頭疼的問題,即如何對其眾多的參數(shù)進(jìn)行正確的設(shè)置。G1的目標(biāo)就是替代CMS,所以在設(shè)計之初就希望降低程序員的負(fù)擔(dān),減少人工的介入。但這并不意味著我們完全不需要了解G1的原理和參數(shù)調(diào)優(yōu)。筆者在實(shí)際工作中遇到過一些因參數(shù)設(shè)置不正確而導(dǎo)致GC停頓時間過長的問題。但要正確設(shè)置參數(shù)并不容易,這里涉及兩個方面:第一,需要對G1的原理熟悉,只有熟悉G1的原理才知道調(diào)優(yōu)的方向;第二,能分析和解讀G1運(yùn)行的日志信息,根據(jù)日志信息找到G1運(yùn)行過程中的異常信息,并推斷哪些參數(shù)可以解決這些異常。
本書嘗試從G1的原理出發(fā),系統(tǒng)地介紹新生代回收、混合回收、Full GC、并發(fā)標(biāo)記、Refine線程等內(nèi)容;同時依托于jdk8u的源代碼介紹Hotspot如何實(shí)現(xiàn)G1,通過對源代碼的分析來了解G1提供了哪些參數(shù)、這些參數(shù)的具體意義;最后本書還設(shè)計了一些示例代碼,給出了G1在運(yùn)行這些示例代碼時的日志,通過日志分析來嘗試調(diào)整參數(shù)并達(dá)到性能優(yōu)化,還分析了參數(shù)調(diào)整可能帶來的負(fù)面影響。
乍聽起來,G1非常復(fù)雜,應(yīng)該會有很多的參數(shù)。實(shí)際上在JDK8的G1實(shí)現(xiàn)中,一共新增了93個參數(shù),其中開發(fā)參數(shù)(develop)有41個,產(chǎn)品參數(shù)(product)有31個,診斷參數(shù)(diagnostic)有9個,實(shí)驗參數(shù)(experimental)有12個。開發(fā)參數(shù)需要在調(diào)試版本中才能進(jìn)行驗證(本書只涉及個別參數(shù)),其余的三類參數(shù)都可以在發(fā)布版本中打開、驗證和使用。本書除了幾個用于驗證的診斷參數(shù)外,覆蓋了發(fā)布版本中涉及的所有參數(shù),為讀者理解G1以及調(diào)優(yōu)G1提供了幫助。
本書共分為12章,主要內(nèi)容如下:
第1章介紹垃圾回收的發(fā)展及使用的算法,同時還介紹一些重要并常見的術(shù)語。該章的知識不僅僅限于本書介紹的G1,對于研讀JVM文章或者JVM源碼都有幫助。
第2章介紹G1中的基本概念,包括分區(qū)、卡表、根集合、線程棧等和垃圾回收相關(guān)的基本知識點(diǎn)。
第3章介紹G1是如何分配對象的,包括TLAB和慢速分配,G1的對象分配和其他垃圾回收器的對象分配非常類似,只不過在分配的時候以分區(qū)為基礎(chǔ),除此之外沒有額外的變化,所以該章知識不僅僅適用于G1也適用于其他垃圾回收器,最后介紹了參數(shù)調(diào)優(yōu),同樣也適用于其他的垃圾回收器。
第4章介紹G1 Refine線程,包括G1如何管理和處理代際引用,從而加快垃圾回收速度,介紹了Refinement調(diào)優(yōu)涉及的參數(shù);雖然CMS也有卡表處理代際引用,但是G1的處理和CMS并不相同,Refine線程是G1新引入的部分。
第5章介紹新生代回收,包括G1如何進(jìn)行新生代回收,包括對象標(biāo)記、復(fù)制、分區(qū)釋放等細(xì)節(jié),還介紹了新生代調(diào)優(yōu)涉及的參數(shù)。
第6章介紹混合回收。主要介紹G1的并發(fā)標(biāo)記算法及其難點(diǎn),以及G1中如何解決這個難點(diǎn),同時介紹了并發(fā)標(biāo)記的步驟:并發(fā)標(biāo)記、Remark(再標(biāo)記)和清理階段;最后還介紹了并發(fā)標(biāo)記的調(diào)優(yōu)參數(shù)。
第7章介紹Full GC。在G1中,F(xiàn)ull GC對整個堆進(jìn)行垃圾回收,該章介紹G1的串行Full GC和JDK 10之后的并行Full GC算法。
第8章介紹垃圾回收過程中如何處理引用,該功能不是G1獨(dú)有的,也適用于其他垃圾回收器。
第9章介紹G1的新特性:字符串去重。根據(jù)OpenJDK的官方文檔,該特性可平均節(jié)約內(nèi)存13%左右,所以這是一個非常有用的特性,值得大家嘗試和使用。另外,該特性和JDK中String類的intern方法有一些類似的地方,所以該章還比較了它們之間的不同。
第10章介紹線程中的安全點(diǎn)。安全點(diǎn)在實(shí)際調(diào)優(yōu)中涉及的并不多,所以很多人并不是特別熟悉。實(shí)際上,垃圾回收發(fā)生時,在進(jìn)入安全點(diǎn)中做了不少的工作,而這些工作基本上是串行進(jìn)行的,這些事情很有可能導(dǎo)致垃圾回收的時間過長。該章除了介紹如何進(jìn)入安全點(diǎn)之外,還介紹了在安全點(diǎn)中做的一些回收工作,以及當(dāng)發(fā)現(xiàn)它們導(dǎo)致GC過長時該如何調(diào)優(yōu)。
第11章介紹如何選擇垃圾回收器,以及選擇G1遇到問題需要調(diào)優(yōu)時我們該如何下手。該章屬于理論性的指導(dǎo),在實(shí)際工作中需要根據(jù)本書提到的參數(shù)正面影響和負(fù)面影響綜合考慮,并不斷調(diào)整。
第12章介紹了下一代垃圾回收器Shenandoah和ZGC。G1作為發(fā)揮重要作用的垃圾回收器仍有不足之處,因此未來的垃圾回收器仍會繼續(xù)發(fā)展,該章介紹了下一代垃圾回收器Shenandoah和ZGC對G1的改進(jìn)之處及其工作原理。
本書的附錄包含如下內(nèi)容:
附錄A介紹如何開始閱讀和調(diào)試JVM代碼。這里簡單介紹了G1的代碼架構(gòu)和組織形式。另外簡單介紹了Linux的調(diào)試工具GDB,這個工具對于想要了解JVM細(xì)節(jié)的同學(xué)必不可少。
附錄B介紹如何使用NMT對JVM內(nèi)存進(jìn)行跟蹤和調(diào)試。這個知識對于想要深入理解JVM內(nèi)存的管理非常有幫助,另外在實(shí)際工作中,特別是JDK升級中我們必須比較同一應(yīng)用在不同JVM運(yùn)行情況下的內(nèi)存使用。
附錄C介紹了Java程序員閱讀JVM時需要知道的一些C++知識。這里并未羅列C++的語法以及語法特性,僅僅介紹一些C++語言特有的、而Java語言沒有的語法,或者Java語言中的使用或理解不同于C++語言的部分語法。這個知識是為Java程序員準(zhǔn)備的,特別是為在閱讀JVM代碼時準(zhǔn)備的。
G1在JDK 6中出現(xiàn),經(jīng)歷JDK 7的發(fā)展,到JDK 8已經(jīng)相當(dāng)成熟,在JDK 9之后G1就作為JVM的默認(rèn)垃圾回收器。JDK 8作為Oracle公司長期支持的版本,本書主要基于JDK 8進(jìn)行分析,所用的版本是jdk8u60。在第7章中為了擴(kuò)展讀者的視野,追蹤最新的技術(shù),還介紹了JDK 10中的并行Full GC。讀者可以自行到OpenJDK的官網(wǎng)下載,也可以使用筆者在GitHub中的備份(JDK 8: https://github.com/chenghanpeng/jdk8u60,JDK 10:https://github.com/chenghanpeng/jdk10u)。
本書在分析源碼的時候會給出源代碼所屬的文件,例如在介紹G1分區(qū)類型時,指出源代碼位于hotspot/src/share/vm/gc_implementation/g1/heapRegionType.hpp,這里的hotspot就是你下載的jdk8u60代碼里面的一級目錄。如果你不希望在本地保留源代碼可以直接瀏覽網(wǎng)址https://github.com/chenghanpeng/jdk8u60,在此你可以找到這個一級目錄hotspot,然后通過逐個查看子目錄src、share、vm、gc_implementation、g1就可以找到源文件heapRegionType.hpp。
需要注意的是,在分析源碼的時候為了節(jié)約篇幅,通常會對原始的代碼進(jìn)行一些調(diào)整,例如刪除一些大括號、統(tǒng)計信息、打印信息,或者刪除一些不影響理解原理和算法的代碼,大家在和源碼比較時需要注意這些變化。另外對于定義在header文件和cpp文件中的一些函數(shù),為了使代碼緊湊,通常會忽略頭文件中的定義,直接按照C++的語法,即類名::成員函數(shù)的方式給出源碼,這樣的代碼可能和原文件不完全一致,但是完全符合C++語言的組織,閱讀源碼時要注意將定義和實(shí)現(xiàn)分開。
由于筆者水平有限,時間倉促,書中難免出現(xiàn)一些錯誤或者不準(zhǔn)確的地方,懇請讀者批評指正。可以通過https://github.com/chenghanpeng/jdk8u60/issues進(jìn)行討論,期待能夠得到讀者朋友們的真情反饋,在技術(shù)道路上互勉共進(jìn)。
在本書的寫作過程中,得到了很多朋友以及同事的幫助和支持,在此表示衷心的感謝!
感謝吳怡編輯的支持和鼓勵,在寫作過程中給出了非常多的意見和建議,不厭其煩地認(rèn)真和筆者溝通,力爭做到清晰、準(zhǔn)確、無誤。感謝你的耐心,為你的專業(yè)精神致敬!
感謝我的家人,特別是謝謝我的兒子,體諒爸爸犧牲了陪伴你的時間。有了你們的支持和幫助,我才有時間和精力去完成寫作。
Contents 目 錄
前 言
第1章 垃圾回收概述? 1
1.1 Java發(fā)展概述? 1
1.2 本書常見術(shù)語? 4
1.3 回收算法概述? 6
1.3.1 分代管理算法? 7
1.3.2 復(fù)制算法? 7
1.3.3 標(biāo)記清除? 8
1.3.4 標(biāo)記壓縮? 9
1.3.5 算法小結(jié)? 9
1.4 JVM垃圾回收器概述? 9
1.4.1 串行回收? 9
1.4.2 并行回收? 10
1.4.3 并發(fā)標(biāo)記回收? 10
1.4.4 垃圾優(yōu)先回收? 10
第2章 G1的基本概念? 14
2.1 分區(qū)? 14
2.2 G1停頓預(yù)測模型? 20
2.3 卡表和位圖? 22
2.4 對象頭? 24
2.5 內(nèi)存分配和管理? 27
2.6 線程? 30
2.6.1 棧幀? 32
2.6.2 句柄? 34
2.6.3 JVM本地方法棧中的對象? 36
2.6.4 Java本地方法棧中的對象? 40
2.7 日志解讀? 40
2.8 參數(shù)介紹和調(diào)優(yōu)? 41
第3章 G1的對象分配? 43
3.1 對象分配概述? 43
3.2 快速分配? 46
3.3 慢速分配? 56
3.3.1 大對象分配? 58
3.3.2 最后的分配嘗試? 60
3.4 G1垃圾回收的時機(jī)? 61
3.4.1 分配時發(fā)生回收? 61
3.4.2 外部調(diào)用的回收? 61
3.5 參數(shù)介紹和調(diào)優(yōu)? 62
第4章 G1的Ref?ine線程? 64
4.1 記憶集? 64
4.2 Ref?ine線程的功能及原理? 72
4.2.1 抽樣線程? 72
4.2.2 管理RSet? 74
4.2.3 Mutator處理DCQ? 78
4.2.4 Ref?ine線程的工作原理? 78
4.3 Ref?inement Zone? 85
4.4 RSet涉及的寫屏障? 86
4.5 日志解讀? 87
4.6 參數(shù)介紹和調(diào)優(yōu)? 90
第5章 新生代回收? 93
5.1 YGC算法概述? 93
5.2 YGC代碼分析? 96
5.2.1 并行任務(wù)? 96
5.2.2 其他處理? 115
5.3 YGC算法演示? 116
5.3.1 選擇CSet? 117
5.3.2 根處理? 117
5.3.3 RSet處理? 118
5.3.4 復(fù)制? 119
5.3.5 Redirty? 120
5.3.6 釋放空間? 120
5.4 日志解讀? 121
5.4.1 YGC日志? 121
5.4.2 大對象日志分析? 125
5.4.3 對象年齡日志分析? 125
5.5 參數(shù)介紹和調(diào)優(yōu)? 126
第6章 混合回收? 129
6.1 并發(fā)標(biāo)記算法詳解? 130
6.2 并發(fā)標(biāo)記算法的難點(diǎn)? 133
6.2.1 三色標(biāo)記法? 133
6.2.2 難點(diǎn)示意圖? 133
6.2.3 再談寫屏障? 135
6.3 G1中混合回收的步驟? 141
6.4 混合回收中并發(fā)標(biāo)記處理的線程? 145
6.4.1 并發(fā)標(biāo)記線程啟動的時機(jī)? 147
6.4.2 根掃描子階段? 148
6.4.3 并發(fā)標(biāo)記子階段? 152
6.4.4 再標(biāo)記子階段? 159
6.4.5 清理子階段? 160
6.4.6 啟動混合收集? 167
6.5 并發(fā)標(biāo)記算法演示? 170
6.5.1 初始標(biāo)記子階段? 171
6.5.2 根掃描子階段? 171
6.5.3 并發(fā)標(biāo)記子階段? 171
6.5.4 再標(biāo)記子階段? 172
6.5.5 清理子階段? 173
6.6 GC活動圖? 174
6.7 日志解讀? 174
6.8 參數(shù)優(yōu)化? 178
第7章 Full GC? 181
7.1 Evac失敗? 181
7.2 串行FGC? 187
7.2.1 標(biāo)記活躍對象? 188
7.2.2 計算對象的新地址? 190
7.2.3 更新引用對象的地址? 190
7.2.4 移動對象完成壓縮? 193
7.2.5 后處理? 194
7.3 并行FGC? 196
7.3.1 并行標(biāo)記活躍對象? 197
7.3.2 計算對象的新地址? 198
7.3.3 更新引用對象的地址 ?200
7.3.4 移動對象完成壓縮? 200
7.3.5 后處理? 201
7.4 日志解讀? 201
7.5 參數(shù)介紹和調(diào)優(yōu)? 202
第8章 G1中的引用處理? 203
8.1 引用概述? 203
8.2 可回收對象發(fā)現(xiàn)? 207
8.3 在GC時的處理發(fā)現(xiàn)列表? 210
8.4 重新激活可達(dá)的引用? 214
8.5 日志解讀? 215
8.6 參數(shù)介紹和調(diào)優(yōu)? 215
第9章 G1的新特性:字符串去重? 217
9.1 字符串去重概述? 217
9.2 日志解讀? 220
9.3 參數(shù)介紹和調(diào)優(yōu)? 222
9.4 字符串去重和String.intern的區(qū)別? 222
9.5 String.intern中的實(shí)現(xiàn)? 223
第10章 線程中的安全點(diǎn)? 226
10.1 安全點(diǎn)的基本概念? 226
10.2 G1并發(fā)線程進(jìn)入安全點(diǎn)? 227
10.3 解釋線程進(jìn)入安全點(diǎn)? 230
10.4 編譯線程進(jìn)入安全點(diǎn)? 230
10.5 正在執(zhí)行本地代碼的線程進(jìn)入安全點(diǎn)? 233
10.6 安全點(diǎn)小結(jié)? 236
10.7 日志分析? 236
10.8 參數(shù)介紹和調(diào)優(yōu)? 238
第11章 垃圾回收器的選擇? 241
11.1 如何衡量垃圾回收器? 241
11.2 G1調(diào)優(yōu)的方向? 243
第12章 新一代垃圾回收器? 247
12.1 Shenandoah? 247
12.2 ZGC? 258
附錄A 編譯調(diào)試JVM? 262
附錄B 本地內(nèi)存跟蹤? 272
附錄C 閱讀JVM需要了解的C++知識? 276
日志分析服務(wù) LOG JVM
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。