G1 GC提出了不確定性Region,每個(gè)空閑Region不是為某個(gè)固定年代準(zhǔn)備的,它是靈活的,需求驅(qū)動(dòng)的,所以G1 GC代表了先進(jìn)性。本書(shū)主要為學(xué)習(xí)Java語(yǔ)言的學(xué)生、初級(jí)程序員提供GC的使用參考建議及經(jīng)驗(yàn),著重介紹了G1 GC。中國(guó)的軟件開(kāi)發(fā)行業(yè)已經(jīng)有幾十年了,從目前的行業(yè)發(fā)展來(lái)看,單純的軟件公司很難有發(fā)展,目前流行的云計(jì)算、物聯(lián)網(wǎng)企業(yè)實(shí)際上是綜合性IT技術(shù)的整合,這就需要有綜合能力的程序員。本書(shū)作者力求做到知識(shí)的綜合傳播,而不是僅僅針對(duì)Java虛擬機(jī)和GC調(diào)優(yōu)進(jìn)行講解,也力求每一章節(jié)都有實(shí)際的案例支撐。本書(shū)具體包括以下幾方面:JVM基礎(chǔ)知識(shí)、GC基礎(chǔ)知識(shí)、G1 GC的深入介紹、G1 GC調(diào)優(yōu)建議、JDK自帶工具使用介紹等。通讀本書(shū)后,讀者可以深入了解G1 GC性能調(diào)優(yōu)的許多主題及相關(guān)的綜合性知識(shí)。
適讀人群 :初、中級(jí)Java程序員、GC深入學(xué)習(xí)人員
★沒(méi)有一招鮮似的調(diào)優(yōu)秘籍或包羅萬(wàn)象的性能百科
★GC性能調(diào)優(yōu)需要專門(mén)的知識(shí)技能才能解決
★解決的GC性能問(wèn)題越多,技藝才會(huì)越精湛
★《深入理解JVM & G1 GC》不僅有技術(shù)實(shí)踐,還有設(shè)計(jì)原理和目標(biāo)
★讓你深入掌握GC,積累更多技術(shù)與經(jīng)驗(yàn),
★更好地提升產(chǎn)品性能
★序
這是我第一次為人寫(xiě)序,心中不免忐忑,引用余光中先生對(duì)于寫(xiě)序的感受:“我為人寫(xiě)序,于人為略而于文為詳,用意也無(wú)非要就文本去探人本,亦即其藝術(shù)人格;自問(wèn)與中國(guó)傳統(tǒng)的序跋并不相悖,但手段畢竟不同了。”周明耀的書(shū)追求的是人本,我則盡力而為。
周明耀是我以前的同事,我們是認(rèn)識(shí)十多年的朋友,同時(shí)我也是他婚禮的伴郎,見(jiàn)證了他的一步步成長(zhǎng)。周明耀無(wú)論對(duì)工作、技術(shù),或者社會(huì)生活,都有著自己獨(dú)特的見(jiàn)解。他始終對(duì)技術(shù)充滿了敬畏之心,學(xué)無(wú)止境,并付諸實(shí)踐。因此,才有了這本書(shū)。
以我對(duì)GC的理解,想要學(xué)習(xí)GC,首先需要理解為什么需要GC。隨著應(yīng)用程序所應(yīng)對(duì)的業(yè)務(wù)越來(lái)越龐大、復(fù)雜,用戶越來(lái)越多,沒(méi)有GC就不能保證應(yīng)用程序的正常進(jìn)行。而經(jīng)常造成STW的GC又跟不上實(shí)際的需求,所以才會(huì)不斷地嘗試對(duì)GC進(jìn)行優(yōu)化。正如周明耀在文中描述的,HotSpot有這么多的垃圾回收器(SerialGC、ParallelGC、ConcurrentMarkSweepGC),為什么還要發(fā)布GarbageFirst(G1)GC?原因就是這個(gè)。
當(dāng)今的商業(yè)模式,更多依賴市場(chǎng)的力量,不斷淘汰舊的行業(yè),把有限的資源讓給那些競(jìng)爭(zhēng)力更強(qiáng)、利潤(rùn)率更高的企業(yè)。類似地,硅谷也在不斷淘汰過(guò)時(shí)的人員,從全世界吸收新鮮血液。經(jīng)過(guò)半個(gè)多世紀(jì)的發(fā)展,在硅谷地區(qū)形成了只有卓越才能生存的文化理念。本著這樣的理念,GC承擔(dān)了淘汰垃圾、保存優(yōu)良資產(chǎn)的任務(wù)。正如周明耀所說(shuō),隨著G1GC的出現(xiàn),GC從傳統(tǒng)的連續(xù)堆內(nèi)存布局設(shè)計(jì),逐漸走向不連續(xù)內(nèi)存塊,這是通過(guò)引入Region概念實(shí)現(xiàn)的,也就是說(shuō),由一堆不連續(xù)的Region組成了堆內(nèi)存。其實(shí)也不能說(shuō)是不連續(xù)的,只是它從傳統(tǒng)的物理連續(xù)逐漸變?yōu)檫壿嬌系倪B續(xù),這是通過(guò)Region的動(dòng)態(tài)分配方式實(shí)現(xiàn)的,我們可以把一個(gè)Region分配給Eden、Survivor、老年代、大對(duì)象區(qū)間、空閑區(qū)間中的任意一個(gè),而不是固定它的作用,因?yàn)樵绞枪潭ǎ绞谴舭濉?
總的來(lái)說(shuō),本書(shū)對(duì)JavaGC機(jī)制的分析深入淺出,是對(duì)大數(shù)據(jù)Java內(nèi)存回收的優(yōu)秀實(shí)踐。讀完茅塞頓開(kāi)、受益匪淺。很多技術(shù)細(xì)節(jié)應(yīng)用之后,對(duì)產(chǎn)品性能有明顯提升。在此感謝周明耀的分享,希望他能夠?qū)懗龈鄡?yōu)秀的書(shū)籍。
華為南京研究所大數(shù)據(jù)產(chǎn)品部維護(hù)經(jīng)理吳駿
★前言
7歲那年,當(dāng)我合上《上下五千年》一套三冊(cè)全書(shū)時(shí),我對(duì)自己說(shuō),我想當(dāng)個(gè)作家。這一晃27年了,等待了27年,我的第一本書(shū)《大話Java性能優(yōu)化》在2016年4月正式面世,2016年8月第二次印刷,感謝讀者的厚愛(ài)。第一次印刷時(shí)出現(xiàn)一些錯(cuò)別字,請(qǐng)?jiān)従庉嬓」媚铮?9萬(wàn)字對(duì)她來(lái)說(shuō)確實(shí)太多了,這是我的責(zé)任,未來(lái)一定盡全力避免。《深入理解JVM&G1GC》是我的第二本書(shū),也即將面世。對(duì)于我的每一本書(shū),我都懷著忐忑、驚喜的心情,就像第一次面對(duì)我的女兒“小頑子”,給她取這個(gè)小名,是希望她頑強(qiáng)到底,因?yàn)槲蚁嘈牛闳纛B強(qiáng)到底,一切皆有可能。
我喜歡看書(shū),每年購(gòu)買的書(shū)接近100本,也喜歡技術(shù)積累。一直沒(méi)有出書(shū)的想法,直到遇到了電子工業(yè)出版社的董老師,在深圳南湖的一席暢談后,我決定做一位業(yè)余的技術(shù)作家,致力于中國(guó)軟件開(kāi)發(fā)行業(yè)的技術(shù)推廣、普及、推動(dòng)。
每年都要面試很多學(xué)生,我感覺(jué)中國(guó)的大學(xué)不太注重實(shí)際項(xiàng)目開(kāi)發(fā)能力的培養(yǎng),較為教條,這也是我的系統(tǒng)叢書(shū)首先從Java技術(shù)開(kāi)始的原因,它更加接地氣。本書(shū)主要為學(xué)習(xí)Java語(yǔ)言的學(xué)生、初級(jí)程序員提供JVM和GC的使用和優(yōu)化建議及經(jīng)驗(yàn),力求做到知識(shí)的綜合傳播,而不是僅僅針對(duì)Java虛擬機(jī)調(diào)優(yōu)進(jìn)行講解。本書(shū)具體包括以下幾方面:JVM基礎(chǔ)知識(shí)、GC基礎(chǔ)知識(shí)、G1GC的深入介紹、G1GC調(diào)優(yōu)建議、JDK自帶工具使用介紹等。
本書(shū)基于JDK8,總的來(lái)說(shuō),沒(méi)有一招鮮式的性能調(diào)優(yōu)秘籍或包羅萬(wàn)象的性能百科,能讓你搖身一變成為老練的GC性能調(diào)優(yōu)專家。相當(dāng)數(shù)量的GC性能問(wèn)題還需要專門(mén)的知識(shí)技能才能解決。性能調(diào)優(yōu)在很大程度上是一門(mén)藝術(shù)。解決的GC性能問(wèn)題越多,技藝才會(huì)越精湛。我們不只要關(guān)心GC的持續(xù)演進(jìn),也要積極地去了解它的設(shè)計(jì)原理和設(shè)計(jì)目標(biāo)。
最后,自我介紹一下,我叫周明耀,研究生學(xué)歷,12年工作經(jīng)驗(yàn),IBM開(kāi)發(fā)者論壇專家作者。我是一名IT技術(shù)狂熱愛(ài)好者,一名九三學(xué)社社員,一名頑強(qiáng)到底的工程師。我推崇技術(shù)創(chuàng)新、思維創(chuàng)新,對(duì)于新技術(shù)非常的熱愛(ài)。
感謝我的家人,和諧的家庭幫助我完成了這本書(shū)。我的妻子,她美麗、細(xì)心、博學(xué)、偶爾不那么溫柔,但是我很愛(ài)她。我的小頑子,她天生性格很像我,希望她能夠踏踏實(shí)實(shí)做人,保持創(chuàng)新精神,平平安安、健健康康地生活下去。感謝我妻子的父母、我的父母,他們幫我照顧小孩,我才有時(shí)間編寫(xiě)此書(shū)。感謝浙江省特級(jí)教師、杭州高級(jí)化學(xué)老師鄭克良老師,鄭老師的一句永遠(yuǎn)不要放棄,推動(dòng)著我多年的發(fā)展。感謝數(shù)學(xué)老師張老師在公開(kāi)場(chǎng)合對(duì)我智商的褒獎(jiǎng),第一次收獲這樣的贊賞,對(duì)我這樣內(nèi)向的孩子是多么的重要,謝謝。
這本書(shū)獻(xiàn)給我記憶中的爺爺奶奶、外公外婆,你們給我的都是最美的回憶。
我相信這本書(shū)不是終點(diǎn),它是麥克叔叔此生一系列技術(shù)書(shū)籍的一員,咱們下一本書(shū)見(jiàn)。
★周明耀,12年投資銀行項(xiàng)目、分布式計(jì)算項(xiàng)目工作經(jīng)驗(yàn),IBM開(kāi)發(fā)者論壇專欄作者、InfoQ專欄作者。一名IT技術(shù)狂熱愛(ài)好者,一名頑強(qiáng)到底的工程師。推崇技術(shù)創(chuàng)新、思維創(chuàng)新,對(duì)于新技術(shù)非常熱愛(ài),致力于技術(shù)研發(fā)、研究,通過(guò)發(fā)布文章、書(shū)籍、互動(dòng)活動(dòng)的形式積極推廣軟件技術(shù)。歡迎添加微信“michael_tec”,共同探討IT技術(shù)話題。
序 VII
前言 IX
第1章 JVM & GC基礎(chǔ)知識(shí) 1
1.1 引言 2
1.2 基本術(shù)語(yǔ) 3
1.2.1 Java相關(guān)術(shù)語(yǔ) 4
1.2.2 JVM/GC通用術(shù)語(yǔ) 24
1.2.3 G1涉及術(shù)語(yǔ) 56
1.3 本章小結(jié) 62
第2章 JVM & GC深入知識(shí) 63
2.1 Java虛擬機(jī)內(nèi)存模型 64
2.1.1 程序計(jì)數(shù)器 65
2.1.2 虛擬機(jī)棧 66
2.1.3 本地方法棧 72
2.1.4 Java堆 73
2.1.5 方法區(qū) 79
2.2 垃圾收集算法 82
2.2.1 引用計(jì)數(shù)法 82
2.2.2 根搜索算法 83
2.2.3 標(biāo)記-清除算法(Mark-Sweep) 85
2.2.4 復(fù)制算法(Copying) 87
2.2.5 標(biāo)記-壓縮算法(Mark-Compact) 89
2.2.6 增量算法(Incremental Collecting) 90
2.2.7 分代收集算法(Generational Collecting) 91
2.3 Garbage Collection 92
2.3.1 GC概念 92
2.3.2 垃圾收集器分類 93
2.3.3 Serial收集器 94
2.3.4 ParNew收集器 96
2.3.5 Parallel收集器 99
2.3.6 CMS收集器 102
2.3.7 Garbage First(G1)GC 106
2.4 常見(jiàn)問(wèn)題解析 112
2.4.1 jmap -heap或-histo不能用 112
2.4.2 YGC越來(lái)越慢 112
2.4.3 Java永久代去哪兒了 114
2.5 本章小結(jié) 116
第3章 G1 GC應(yīng)用示例 117
3.1 范例程序 118
3.2 選項(xiàng)解釋及應(yīng)用 124
3.3 本章小結(jié) 166
第4章 深入G1 GC 167
4.1 G1 GC概念簡(jiǎn)述 168
4.1.1 背景知識(shí) 168
4.1.2 G1的垃圾回收機(jī)制 169
4.1.3 G1的區(qū)間設(shè)計(jì)靈感 169
4.2 G1 GC分代管理 172
4.2.1 年輕代 172
4.2.2 年輕代回收暫停 173
4.2.3 大對(duì)象區(qū)間 174
4.2.4 混合回收暫停 176
4.2.5 回收集合及其重要性 178
4.2.6 RSet及其重要性 178
4.2.7 并行標(biāo)記循環(huán) 182
4.2.8 評(píng)估失敗和完全回收 186
4.3 G1 GC使用場(chǎng)景 186
4.4 G1 GC論文原文翻譯(部分) 187
4.4.1 開(kāi)題 187
4.4.2 數(shù)據(jù)結(jié)構(gòu)/機(jī)制 188
4.4.3 未來(lái)展望 190
4.5 本章小結(jié) 191
第5章 G1 GC性能優(yōu)化方案 192
5.1 G1的年輕代回收 193
5.2 年輕代優(yōu)化 203
5.3 并行標(biāo)記階段優(yōu)化 205
5.4 混合回收階段 207
5.4.1 初步介紹 207
5.4.2 深入介紹 208
5.5 如何避免出現(xiàn)GC失敗 210
5.6 引用處理 211
5.6.1 觀察引用處理 212
5.6.2 引用處理優(yōu)化 213
5.7 本章小結(jié) 214
第6章 JVM診斷工具使用介紹 215
6.1 SA基礎(chǔ)介紹 216
6.2 SA工具使用實(shí)踐 217
6.2.1 如何啟動(dòng)SA 217
6.2.2 SA原理及使用介紹 222
6.3 其他工具介紹 231
6.3.1 GCHisto 231
6.3.2 JConsole 232
6.3.3 VisualVM 236
6.4 本章小結(jié) 238
2.2.1 引用計(jì)數(shù)法
引用計(jì)數(shù)法(Reference Counting)在GC執(zhí)行垃圾回收之前,首先需要區(qū)分出內(nèi)存中哪些是存活對(duì)象,哪些是已經(jīng)死亡的對(duì)象。只有被標(biāo)記為已經(jīng)死亡的對(duì)象,GC才會(huì)在執(zhí)行垃圾回收時(shí),釋放掉其所占用的內(nèi)存空間,因此這個(gè)過(guò)程我們可以稱為垃圾標(biāo)記階段。
引用計(jì)數(shù)器的實(shí)現(xiàn)很簡(jiǎn)單,對(duì)于一個(gè)對(duì)象A,只要有任何一個(gè)對(duì)象引用了A,則A的引用計(jì)數(shù)器就加1,當(dāng)引用失效時(shí),引用計(jì)數(shù)器就減1。只要對(duì)象A的引用計(jì)數(shù)器的值為0,則對(duì)象A就不可能再被使用。也就是說(shuō),引用計(jì)數(shù)器的實(shí)現(xiàn)只需要為每個(gè)對(duì)象配置一個(gè)整形的計(jì)數(shù)器即可。引用計(jì)數(shù)器算法的一大優(yōu)勢(shì)就是不用等待內(nèi)存不夠用的時(shí)候,才進(jìn)行垃圾的回收,完全可以在賦值操作的同時(shí)檢查計(jì)數(shù)器是否為0,如果是的話就可以立即回收。
但是引用計(jì)數(shù)器有一個(gè)嚴(yán)重的問(wèn)題,即無(wú)法處理循環(huán)引用的情況。一個(gè)簡(jiǎn)單的循環(huán)引用問(wèn)題的描述如下:有對(duì)象A和對(duì)象B,對(duì)象A中含有對(duì)象B的引用,對(duì)象B中含有對(duì)象A的引用。此時(shí),對(duì)象A和對(duì)象B的引用計(jì)數(shù)器都不為0,但是在系統(tǒng)中卻不存在任何第3個(gè)對(duì)象引用了A或B。也就是說(shuō),A和B是應(yīng)該被回收的垃圾對(duì)象,但由于垃圾對(duì)象間相互引用,從而使垃圾回收器無(wú)法識(shí)別,引起內(nèi)存泄漏。
如圖2-6所示,構(gòu)造了一個(gè)列表,將最后一個(gè)元素的next屬性指向第一個(gè)元素,即引用第一個(gè)元素,從而構(gòu)成循環(huán)引用。這個(gè)時(shí)候如果將列表的頭head賦值為null,此時(shí)列表的各個(gè)元素的計(jì)數(shù)器都不為0,同時(shí)也失去了對(duì)列表的引用控制,從而導(dǎo)致列表元素不能被回收。
引用計(jì)數(shù)器擁有一些特性,首先它需要單獨(dú)的字段存儲(chǔ)計(jì)數(shù)器,這樣的做法增加了存儲(chǔ)空間的開(kāi)銷。其次,每次賦值都需要更新計(jì)數(shù)器,這增加了時(shí)間開(kāi)銷。再者,垃圾對(duì)象便于辨識(shí),只要計(jì)數(shù)器為0,就可作為垃圾回收。接下來(lái)它能方便及時(shí)地回收垃圾,沒(méi)有延遲性。最后不能解決循環(huán)引用的問(wèn)題。正是由于最后一條致命缺陷,導(dǎo)致在Java的垃圾回收器中沒(méi)有使用這類算法。
2.2.2 根搜索算法
HotSpot和大部分JVM都是使用根搜索算法作為垃圾標(biāo)記的算法實(shí)現(xiàn)。前面介紹過(guò)的引用計(jì)數(shù)算法盡管實(shí)現(xiàn)簡(jiǎn)單,執(zhí)行效率也不錯(cuò),但是該算法本身卻存在一個(gè)較大的弊端,甚至?xí)绊懙嚼鴺?biāo)記的準(zhǔn)確性。由于引用計(jì)數(shù)算法會(huì)為程序中的每一個(gè)對(duì)象都創(chuàng)建一個(gè)私有的引用計(jì)數(shù)器,當(dāng)目標(biāo)對(duì)象被其他存活對(duì)象引用時(shí),引用計(jì)數(shù)器中的值則會(huì)加1,不再引用時(shí)便會(huì)減1,當(dāng)引用計(jì)數(shù)器中的值為0的時(shí)候,就意味著該對(duì)象已經(jīng)不再被任何存活對(duì)象引用,可以被標(biāo)記為垃圾對(duì)象。采用這種方式看起來(lái)似乎沒(méi)有任何問(wèn)題,但是如果一些明顯已經(jīng)死亡了的對(duì)象盡管沒(méi)有被任何的存活對(duì)象引用,但是它們彼此之間卻存在相互引用時(shí),引用計(jì)數(shù)器中的值則永遠(yuǎn)不會(huì)為0,這樣便會(huì)導(dǎo)致GC在執(zhí)行內(nèi)存回收時(shí)永遠(yuǎn)無(wú)法釋放掉這種無(wú)用對(duì)象所占用的內(nèi)存空間,極有可能引發(fā)內(nèi)存泄漏。
相對(duì)于引用計(jì)數(shù)算法而言,根搜索算法不僅同樣具備實(shí)現(xiàn)簡(jiǎn)單和執(zhí)行高效等特點(diǎn),更重要的是該算法可以有效地解決在引用計(jì)數(shù)算法中一些已經(jīng)死亡的對(duì)象因相互引用而導(dǎo)致的無(wú)法正確被標(biāo)記的問(wèn)題,防止內(nèi)存泄漏的發(fā)生。簡(jiǎn)單來(lái)說(shuō),根搜索算法是以根對(duì)象集合為起始點(diǎn),按照從上至下的方式搜索被根對(duì)象集合所連接的目標(biāo)對(duì)象是否可達(dá)(使用根搜索算法后,內(nèi)存中的存活對(duì)象都會(huì)被根對(duì)象集合直接或間接連接著),如果目標(biāo)對(duì)象不可達(dá),就意味著該對(duì)象已經(jīng)死亡,便可以在instanceOopDesc[ HotSpot在C++代碼中用instanceOopDesc類來(lái)表示Java對(duì)象,而該類繼承oopDesc,所以HotSpot中的Java對(duì)象也自然擁有oopDesc所聲明的頭部。]的Mark World中將其標(biāo)記為垃圾對(duì)象。在根搜索算法中,只有能夠被根對(duì)象集合直接或者間接連接的對(duì)象才是存活對(duì)象。在HotSpot中,根對(duì)象集合中包含了5個(gè)元素,Java棧內(nèi)的對(duì)象引用、本地方法棧內(nèi)的對(duì)象引用、運(yùn)行時(shí)常量池中的對(duì)象引用、方法區(qū)中類靜態(tài)屬性的對(duì)象引用以及與一個(gè)類對(duì)應(yīng)的唯一數(shù)據(jù)類型的Class對(duì)象。