內容介紹
G1GC提齣瞭不確定性Region,每個空閑Region不是為某個固定年代準備的,它是靈活的,需求驅動的,所以G1GC代錶瞭XJ性。 《深入理解JVM&G1GC》主要為學習Java語言的學生、初級程序員提供GC的使用參考建議及經驗,著重介紹瞭G1GC。中國的軟件開發行業已經有幾十年瞭,從目前的行業發展來看,單純的軟件公司很難有發展,目前流行的雲計算、物聯網企業實際上是綜閤性IT技術的整閤,這J需要有綜閤能力的程序員。《深入理解JVM&G1GC》作者力求做到知識的綜閤傳播,而不是僅僅針對Java虛擬機和GC調優進行講解,也力求每一章節都有實際的案例支撐。《深入理解JVM&G1GC》具體包括以下幾方麵:JVM基礎知識、GC基礎知識、G1GC的深入介紹、G1GC調優建議、JDK自帶工具使用介紹等。 通讀《深入理解JVM&G1GC》後,讀者可以深入瞭解G1GC性能調優的許多主題及相關的綜閤性知識。讀者也可以把《深入理解JVM&G1GC》作為參考,對於感興趣的主題,直接跳到相應章節尋找答案。
關聯推薦
★沒有一招鮮似的調優秘籍或包羅萬象的性能百科
★GC性能調優需要專門的知識技能纔能解決
★解決的GC性能問題越多,技藝纔會越精湛
★《深入理解JVM & G1 GC》不僅有技術實踐,還有設計原理和目標
★讓你深入掌握GC,積纍更多技術與經驗,
★更好地提升産品性能
目錄
目 錄 序 VII 前言 IX D1章 JVM & GC基礎知識 1 1.1 引言 2 1.2 基本術語 3 1.2.1 Java相關術語 4 1.2.2 JVM/GC通用術語 24 1.2.3 G1涉及術語 56 1.3 本章小結 62 D2章 JVM & GC深入知識 63 2.1 Java虛擬機內存模型 64 2.1.1 程序計數器 65目 錄
序 VII
前言 IX
D1章 JVM & GC基礎知識 1
1.1 引言 2
1.2 基本術語 3
1.2.1 Java相關術語 4
1.2.2 JVM/GC通用術語 24
1.2.3 G1涉及術語 56
1.3 本章小結 62
D2章 JVM & GC深入知識 63
2.1 Java虛擬機內存模型 64
2.1.1 程序計數器 65
2.1.2 虛擬機棧 66
2.1.3 本地方法棧 72
2.1.4 Java堆 73
2.1.5 方法區 79
2.2 垃圾收集算法 82
2.2.1 引用計數法 82
2.2.2 根搜索算法 83
2.2.3 標記-清除算法(Mark-Sweep) 85
2.2.4 復製算法(Copying) 87
2.2.5 標記-壓縮算法(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 常見問題解析 112
2.4.1 jmap -heap或-histo不能用 112
2.4.2 YGC越來越慢 112
2.4.3 JavaYJ代去哪兒瞭 114
2.5 本章小結 116
D3章 G1 GC應用示例 117
3.1 範例程序 118
3.2 選項解釋及應用 124
3.3 本章小結 166
D4章 深入G1 GC 167
4.1 G1 GC概念簡述 168
4.1.1 背景知識 168
4.1.2 G1的垃圾迴收機製 169
4.1.3 G1的區間設計靈感 169
4.2 G1 GC分代管理 172
4.2.1 年輕代 172
4.2.2 年輕代迴收暫停 173
4.2.3 大對象區間 174
4.2.4 混閤迴收暫停 176
4.2.5 迴收集閤及其重要性 178
4.2.6 RSet及其重要性 178
4.2.7 並行標記循環 182
4.2.8 評估失敗和WQ迴收 186
4.3 G1 GC使用場景 186
4.4 G1 GC論文原文翻譯(部分) 187
4.4.1 開題 187
4.4.2 數據結構/機製 188
4.4.3 未來展望 190
4.5 本章小結 191
D5章 G1 GC性能優化方案 192
5.1 G1的年輕代迴收 193
5.2 年輕代優化 203
5.3 並行標記階段優化 205
5.4 混閤迴收階段 207
5.4.1 初步介紹 207
5.4.2 深入介紹 208
5.5 如何避免齣現GC失敗 210
5.6 引用處理 211
5.6.1 觀察引用處理 212
5.6.2 引用處理優化 213
5.7 本章小結 214
D6章 JVM診斷工具使用介紹 215
6.1 SA基礎介紹 216
6.2 SA工具使用實踐 217
6.2.1 如何啓動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 本章小結 238 顯示全部信息
在綫試讀
2.2.1 引用計數法 引用計數法(Reference Counting)在GC執行垃圾迴收之前,1先需要區分齣內存中哪些是存活對象,哪些是已經死亡的對象。隻有被標記為已經死亡的對象,GC纔會在執行垃圾迴收時,釋放掉其所占用的內存空間,因此這個過程我們可以稱為垃圾標記階段。 引用計數器的實現很簡單,對於一個對象A,隻要有任何一個對象引用瞭A,則A的引用計數器J加1,D引用失效時,引用計數器J減1。隻要對象A的引用計數器的值為0,則對象AJ不可能再被使用。也J是說,引用計數器的實現隻需要為每個對象配置一個整形的計數器即可。引用計數器算法的一大優勢J是不用等待內存不夠用的時候,纔進行垃圾的迴收,WQ可以在賦值操作的同時檢查計數器是否為0,如果是的話J可以立即迴收。 但是引用計數器有一個嚴重的問題,即無法處理循環引用的情況。一個簡單的循環引用問題的描述如下:有對象A和對象B,對象A中含有對象B的引用,對象B中含有對象A的引用。此時,對象A和對象B的引用計數器都不為0,但是在係統中卻不存在任何D3個對象引用瞭A或B。也J是說,A和B是應該被迴收的垃圾對象,但由於垃圾對象間相互引用,從而使垃圾迴收器無法識彆,引起內存泄漏。 如圖2-6所示,構造瞭一個列錶,將Z後一個元素的next屬性指嚮D一個元素,即引用D一個元素,從而構成循環引用。這個時候如果將列錶的頭head賦值為null,此時列錶的各個元素的計數器都不為0,同時也失去瞭對列錶的引用控製,從而導緻列錶元素不能被迴收。 引用計數器擁有一些特性,1先它需要單D的字段存儲計數器,這樣的做法增加瞭存儲空間的開銷。其次,每次賦值都需要更新計數器,這增加瞭時間開銷。再者,垃圾對象便於辨識,隻要計數器為0,J可作為垃圾迴收。接下來它能方便及時地迴收垃圾,沒有延遲性。Z後不能解決循環引用的問題。正是由於Z後一條緻命缺陷,導緻在Java的垃圾迴收器中沒有使用這類算法。
2.2.1 引用計數法
引用計數法(Reference Counting)在GC執行垃圾迴收之前,1先需要區分齣內存中哪些是存活對象,哪些是已經死亡的對象。隻有被標記為已經死亡的對象,GC纔會在執行垃圾迴收時,釋放掉其所占用的內存空間,因此這個過程我們可以稱為垃圾標記階段。
引用計數器的實現很簡單,對於一個對象A,隻要有任何一個對象引用瞭A,則A的引用計數器J加1,D引用失效時,引用計數器J減1。隻要對象A的引用計數器的值為0,則對象AJ不可能再被使用。也J是說,引用計數器的實現隻需要為每個對象配置一個整形的計數器即可。引用計數器算法的一大優勢J是不用等待內存不夠用的時候,纔進行垃圾的迴收,WQ可以在賦值操作的同時檢查計數器是否為0,如果是的話J可以立即迴收。
但是引用計數器有一個嚴重的問題,即無法處理循環引用的情況。一個簡單的循環引用問題的描述如下:有對象A和對象B,對象A中含有對象B的引用,對象B中含有對象A的引用。此時,對象A和對象B的引用計數器都不為0,但是在係統中卻不存在任何D3個對象引用瞭A或B。也J是說,A和B是應該被迴收的垃圾對象,但由於垃圾對象間相互引用,從而使垃圾迴收器無法識彆,引起內存泄漏。
如圖2-6所示,構造瞭一個列錶,將Z後一個元素的next屬性指嚮D一個元素,即引用D一個元素,從而構成循環引用。這個時候如果將列錶的頭head賦值為null,此時列錶的各個元素的計數器都不為0,同時也失去瞭對列錶的引用控製,從而導緻列錶元素不能被迴收。
引用計數器擁有一些特性,1先它需要單D的字段存儲計數器,這樣的做法增加瞭存儲空間的開銷。其次,每次賦值都需要更新計數器,這增加瞭時間開銷。再者,垃圾對象便於辨識,隻要計數器為0,J可作為垃圾迴收。接下來它能方便及時地迴收垃圾,沒有延遲性。Z後不能解決循環引用的問題。正是由於Z後一條緻命缺陷,導緻在Java的垃圾迴收器中沒有使用這類算法。
2.2.2 根搜索算法
HotSpot和大部分JVM都是使用根搜索算法作為垃圾標記的算法實現。前麵介紹過的引用計數算法盡管實現簡單,執行效率也不錯,但是該算法本身卻存在一個較大的弊端,甚至會影響到垃圾標記的準確性。由於引用計數算法會為程序中的每一個對象都創建一個私有的引用計數器,D目標對象被其他存活對象引用時,引用計數器中的值則會加1,不再引用時便會減1,D引用計數器中的值為0的時候,J意味著該對象已經不再被任何存活對象引用,可以被標記為垃圾對象。采用這種方式看起來似乎沒有任何問題,但是如果一些明顯已經死亡瞭的對象盡管沒有被任何的存活對象引用,但是它們彼此之間卻存在相互引用時,引用計數器中的值則永遠不會為0,這樣便會導緻GC在執行內存迴收時永遠無法釋放掉這種無用對象所占用的內存空間,J有可能引發內存泄漏。
相對於引用計數算法而言,根搜索算法不僅同樣具備實現簡單和執行高效等特點,更重要的是該算法可以有效地解決在引用計數算法中一些已經死亡的對象因相互引用而導緻的無法正確被標記的問題,防止內存泄漏的發生。簡單來說,根搜索算法是以根對象集閤為起始點,按照從上至下的方式搜索被根對象集閤所連接的目標對象是否可達(使用根搜索算法後,內存中的存活對象都會被根對象集閤直接或間接連接著),如果目標對象不可達,J意味著該對象已經死亡,便可以在instanceOopDesc[ HotSpot在C 代碼中用instanceOopDesc類來錶示Java對象,而該類繼承oopDesc,所以HotSpot中的Java對象也自然擁有oopDesc所聲明的頭部。]的Mark World中將其標記為垃圾對象。在根搜索算法中,隻有能夠被根對象集閤直接或者間接連接的對象纔是存活對象。在HotSpot中,根對象集閤中包含瞭5個元素,Java棧內的對象引用、本地方法棧內的對象引用、運行時常量池中的對象引用、方法區中類靜態屬性的對象引用以及與一個類對應的W一數據類型的Class對象。
顯示全部信息
深入理解JVM與G1 GC:一本關於程序運行的藝術與科學的書籍 序言 在數字時代的浪潮中,軟件如同建築,其根基的穩固與效率的卓越,直接決定瞭整個體係的生命力與競爭力。而Java虛擬機(JVM)正是無數軟件的生命搖籃,它以其跨平颱、高效率的特性,成為瞭當前軟件開發領域無可爭議的王者。理解JVM,不僅僅是理解一個技術工具,更是洞察程序運行時內在的復雜機製,掌握優化性能的秘訣,從而寫齣更強大、更具韌性的代碼。 本書《深入理解JVM&G1 GC》並非止步於對JVM錶麵的介紹,而是帶領讀者一層層剝開其神秘的麵紗,深入到內存管理、垃圾迴收、字節碼執行等核心領域,特彆是對現代Java應用中廣泛應用的G1垃圾收集器進行詳盡的解析。我們相信,隻有透徹理解瞭JVM的運作原理,纔能真正站在巨人的肩膀上,去創造更偉大的軟件。 第一部分:JVM的基石——內存模型與對象生命周期 理解JVM,首先要從其內存結構入手。本書將詳細剖析JVM的內存區域劃分,包括程序計數器、虛擬機棧、本地方法棧、堆(Heap)以及方法區(MetaSpace)等。每一塊區域的功能、作用範圍以及生命周期都將一一展開。 程序計數器(Program Counter Register):作為綫程的“指揮官”,它記錄著當前綫程執行的字節碼指令的地址。我們將深入探討其在多綫程環境下如何協同工作,確保綫程執行的獨立性和準確性。 虛擬機棧(JVM Stacks):函數調用、局部變量、操作數棧……虛擬機棧是程序運行時計算過程的直接體現。我們會詳細講解棧幀(Stack Frame)的構成,包括局部變量錶、操作數棧、動態鏈接、方法齣口等,以及棧溢齣(StackOverflowError)産生的根源和常見的排查方法。 本地方法棧(Native Method Stacks):當Java代碼調用本地(非Java)方法時,就需要本地方法棧的協助。我們將簡要介紹其與虛擬機棧的區彆以及在特定場景下的作用。 堆(Heap):堆是Java對象生命周期的中心舞颱,也是垃圾迴收器(GC)的主要戰場。我們將從Java對象的創建過程開始,細緻講解對象的內存布局,包括對象頭、實例數據和對齊填充。這一部分將為後續的垃圾迴收機製打下堅實的基礎。 對象的創建過程:從`new`關鍵字的指令到對象在堆上的實際分配,每一步的細節都至關重要。我們將解析JVM如何進行內存分配(指針碰撞、空閑列錶)以及內存分配的並發安全問題(CAS配解鎖、TLAB)。 對象的內存布局:深入理解對象頭(Mark Word、Klass Point)和實例數據,以及它們如何影響對象的訪問和GC的判斷。 對象的引用類型:強引用、軟引用、弱引用、虛引用,這些不同級彆的引用如何影響對象的生命周期,以及它們在內存管理中的妙用。 方法區(MetaSpace):存儲類信息、常量、靜態變量等。我們將探討其在JDK8後的演進,從永久代(PermGen)到元空間(MetaSpace),以及它們在內存管理上的差異和影響。 第二部分:JVM的守護者——垃圾迴收(GC)機製詳解 垃圾迴收是JVM自動管理內存的關鍵機製,它極大地簡化瞭Java程序員的開發負擔。然而,理解GC的原理,纔能寫齣更高效、更少內存泄漏的代碼。本書將深入剖析GC的各個方麵。 GC的基本概念: 什麼是垃圾:從可達性分析的角度,明確哪些對象是“垃圾”,哪些不是。 GC的算法: 標記-清除(Mark-Sweep):最早的GC算法,易於理解,但也存在效率和碎片化問題。 標記-整理(Mark-Compact):在標記-清除的基礎上進行整理,解決瞭碎片化問題,但增加瞭開銷。 復製(Copying):適用於新生代,效率高,但空間利用率不高。 Java堆的細分: 新生代(Young Generation):Eden空間、Survivor空間(S0、S1)。我們將詳細講解對象在新生代的分配、晉升(Minor GC)以及對象存活周期的判斷。 老年代(Old Generation):對象在新生代中經過多次GC後仍然存活,就會被晉升到老年代。老年代的GC(Major GC/Full GC)策略與新生代有所不同。 Minor GC與Full GC:清晰區分這兩種GC的觸發時機、執行過程以及它們對程序性能的影響。 GC的吞吐量與停頓時間:理解GC性能的兩大指標,以及如何在兩者之間進行權衡。 第三部分:現代Java的利器——G1 GC深度解析 G1(Garbage-First)垃圾收集器是Oracle JDK 7u4中提供,並在JDK 9+中成為默認的垃圾收集器。它旨在解決傳統GC在處理大堆時齣現的長時間停頓問題,提供可預測的GC暫停時間。本書將花費大量篇幅,帶領讀者深入理解G1 GC的內部機製。 G1 GC的設計理念與目標: 分代區域(Region):G1 GC打破瞭傳統的固定代際劃分,將堆劃分為大小相等的獨立區域,每個區域可以根據需要扮演新生代或老年代的角色。 區域劃分的優勢:如何通過動態調整區域的角色來更好地管理內存。 軟實時目標(Soft Real-time Goals):G1 GC如何通過用戶設定的暫停時間目標(`-XX:MaxGCPauseMillis`)來控製GC的行為。 G1 GC的工作流程: Young GC(Garbage-First Young GC): 標記(Marking):G1如何使用SAT(Snapshot-At-The-Beginning)技術在Young GC開始時就捕獲堆的快照,以避免並發問題。 復製(Copying):當Young GC發生時,G1如何將存活的對象從Young Regions復製到Survivor Regions(或直接晉升到Old Regions)。 Remembered Sets(RSets):理解GC日誌中常見的“RSet”是什麼,以及它是如何幫助G1 GC高效地追蹤跨區域引用的。 Eager Reclaim:G1如何快速迴收那些已經完全為空的Young Regions。 Concurrent Mark Cycle(並發標記周期): 初始標記(Initial Mark):一個短暫的STW(Stop-The-World)階段,標記與GC Roots直接相連的對象。 並發標記(Concurrent Marking):GC綫程與應用綫程並發執行,遍曆整個堆,標記所有存活的對象。 再次標記(Remark):又一個短暫的STW階段,處理在並發標記期間發生變化的引用。 清理(Cleanup):清理不再使用的對象,並計算區域的“可迴收量”(Garbage First)。 Mixed GC(混閤收集): 何時觸發:當並發標記周期結束後,G1會選擇一些“垃圾最多”(Garbage First)的Old Regions與Young Regions一起進行迴收。 如何選擇Regions:G1如何根據用戶設定的暫停時間目標(`-XX:MaxGCPauseMillis`)來動態選擇混閤迴收的Region集閤,以平衡吞吐量和停頓時間。 迴收過程:混閤迴收的過程結閤瞭Young GC的復製算法和Old GC的標記-整理思想。 G1 GC的調優參數: `-XX:MaxGCPauseMillis`:設置GC的最大暫停時間目標。 `-XX:NewRatio`:雖然G1不是嚴格分代的,但這個參數仍然可以影響新生代和老年代的大緻比例。 `-XX:G1HeapRegionSize`:設置G1區域的大小。 `-XX:ConcGCThreads`:設置並發GC綫程的數量。 `-XX:ParallelGCThreads`:設置並行GC綫程的數量。 `-XX:InitiatingHeapOccupancyPercent`:觸發並發標記周期的堆占用率閾值。 G1 GC的常見問題與診斷: GC日誌分析:如何解讀G1 GC的日誌,理解GC事件的發生、耗時和內存變化。 GC暫停過長:如何分析導緻GC暫停過長的原因(如:集閤瞭太多Old Regions、並發標記耗時過長等)。 內存泄漏的排查:雖然G1是自動內存管理,但內存泄漏仍然可能發生。我們將介紹如何通過GC日誌和工具來排查。 第四部分:理解字節碼與性能調優 掌握瞭JVM的內存模型和GC機製,我們就可以更深入地理解Java程序的執行過程,並在此基礎上進行性能調優。 Java字節碼(Java Bytecode): JVM的語言:理解Java代碼是如何被編譯成字節碼,並在JVM中執行的。 常用字節碼指令:介紹一些關鍵的字節碼指令,如`new`、`getfield`、`putfield`、`invokevirtual`等,瞭解它們與Java語言特性的對應關係。 指令重排序:在多綫程環境下,JVM和CPU如何對指令進行重排序,以及這可能帶來的潛在問題。 JIT編譯器(Just-In-Time Compiler): C1編譯器(Client Compiler):啓動速度快,但優化程度較低。 C2編譯器(Server Compiler):優化程度高,但啓動速度慢。 分層編譯(Tiered Compilation):JVM如何結閤C1和C2編譯器的優點,實現更優的性能。 熱點代碼(Hotspot Code):JIT編譯器如何識彆並優化經常被執行的代碼。 性能調優實戰: 內存分析工具:介紹Arthas、MAT(Memory Analyzer Tool)等工具,如何利用它們進行堆轉儲分析,查找內存泄漏和內存占用過高的對象。 CPU性能分析工具:介紹JProfiler、YourKit等工具,如何分析CPU占用率高的綫程和方法。 GC日誌分析實戰:結閤實際GC日誌,演示如何找齣GC的瓶頸。 避免常見的性能陷阱:如過度創建對象、頻繁的字符串拼接、不恰當的同步機製等。 結語 《深入理解JVM&G1 GC》旨在為讀者提供一個全麵、深入的JVM和G1 GC的認知框架。我們不追求“一招鮮”的速成技巧,而是希望通過對底層原理的透徹講解,幫助讀者建立起獨立思考和解決問題的能力。在復雜的軟件工程領域,理解“為什麼”比“怎麼做”更加重要。 掌握JVM和G1 GC的精髓,意味著你能夠寫齣更穩定、更高效的Java程序,更能從容應對各種性能挑戰。希望本書能成為你探索Java世界,不斷提升技術境界的有力夥伴。讓我們一起,在程序運行的藝術與科學中,探索無限可能。