- JVM G1源碼分析和調優
- 彭成寒
- 2703字
- 2019-04-22 18:14:48
前言
G1是目前最成熟的垃圾回收器,已經廣泛應用在眾多公司的生產環境中。我們知道,CMS作為使用最為廣泛的垃圾回收器,也有令人頭疼的問題,即如何對其眾多的參數進行正確的設置。G1的目標就是替代CMS,所以在設計之初就希望降低程序員的負擔,減少人工的介入。但這并不意味著我們完全不需要了解G1的原理和參數調優。筆者在實際工作中遇到過一些因參數設置不正確而導致GC停頓時間過長的問題。但要正確設置參數并不容易,這里涉及兩個方面:第一,需要對G1的原理熟悉,只有熟悉G1的原理才知道調優的方向;第二,能分析和解讀G1運行的日志信息,根據日志信息找到G1運行過程中的異常信息,并推斷哪些參數可以解決這些異常。
本書嘗試從G1的原理出發,系統地介紹新生代回收、混合回收、Full GC、并發標記、Refine線程等內容;同時依托于jdk8u的源代碼介紹Hotspot如何實現G1,通過對源代碼的分析來了解G1提供了哪些參數、這些參數的具體意義;最后本書還設計了一些示例代碼,給出了G1在運行這些示例代碼時的日志,通過日志分析來嘗試調整參數并達到性能優化,還分析了參數調整可能帶來的負面影響。
乍聽起來,G1非常復雜,應該會有很多的參數。實際上在JDK8的G1實現中,一共新增了93個參數,其中開發參數(develop)有41個,產品參數(product)有31個,診斷參數(diagnostic)有9個,實驗參數(experimental)有12個。開發參數需要在調試版本中才能進行驗證(本書只涉及個別參數),其余的三類參數都可以在發布版本中打開、驗證和使用。本書除了幾個用于驗證的診斷參數外,覆蓋了發布版本中涉及的所有參數,為讀者理解G1以及調優G1提供了幫助。
本書共分為12章,主要內容如下:
·第1章介紹垃圾回收的發展及使用的算法,同時還介紹一些重要并常見的術語。該章的知識不僅僅限于本書介紹的G1,對于研讀JVM文章或者JVM源碼都有幫助。
·第2章介紹G1中的基本概念,包括分區、卡表、根集合、線程棧等和垃圾回收相關的基本知識點。
·第3章介紹G1是如何分配對象的,包括TLAB和慢速分配,G1的對象分配和其他垃圾回收器的對象分配非常類似,只不過在分配的時候以分區為基礎,除此之外沒有額外的變化,所以該章知識不僅僅適用于G1也適用于其他垃圾回收器,最后介紹了參數調優,同樣也適用于其他的垃圾回收器。
·第4章介紹G1 Refine線程,包括G1如何管理和處理代際引用,從而加快垃圾回收速度,介紹了Refinement調優涉及的參數;雖然CMS也有卡表處理代際引用,但是G1的處理和CMS并不相同,Refine線程是G1新引入的部分。
·第5章介紹新生代回收,包括G1如何進行新生代回收,包括對象標記、復制、分區釋放等細節,還介紹了新生代調優涉及的參數。
·第6章介紹混合回收。主要介紹G1的并發標記算法及其難點,以及G1中如何解決這個難點,同時介紹了并發標記的步驟:并發標記、Remark(再標記)和清理階段;最后還介紹了并發標記的調優參數。
·第7章介紹Full GC。在G1中,Full GC對整個堆進行垃圾回收,該章介紹G1的串行Full GC和JDK 10之后的并行Full GC算法。
·第8章介紹垃圾回收過程中如何處理引用,該功能不是G1獨有的,也適用于其他垃圾回收器。
·第9章介紹G1的新特性:字符串去重。根據OpenJDK的官方文檔,該特性可平均節約內存13%左右,所以這是一個非常有用的特性,值得大家嘗試和使用。另外,該特性和JDK中String類的intern方法有一些類似的地方,所以該章還比較了它們之間的不同。
·第10章介紹線程中的安全點。安全點在實際調優中涉及的并不多,所以很多人并不是特別熟悉。實際上,垃圾回收發生時,在進入安全點中做了不少的工作,而這些工作基本上是串行進行的,這些事情很有可能導致垃圾回收的時間過長。該章除了介紹如何進入安全點之外,還介紹了在安全點中做的一些回收工作,以及當發現它們導致GC過長時該如何調優。
·第11章介紹如何選擇垃圾回收器,以及選擇G1遇到問題需要調優時我們該如何下手。該章屬于理論性的指導,在實際工作中需要根據本書提到的參數正面影響和負面影響綜合考慮,并不斷調整。
·第12章介紹了下一代垃圾回收器Shenandoah和ZGC。G1作為發揮重要作用的垃圾回收器仍有不足之處,因此未來的垃圾回收器仍會繼續發展,該章介紹了下一代垃圾回收器Shenandoah和ZGC對G1的改進之處及其工作原理。
本書的附錄包含如下內容:
·附錄A介紹如何開始閱讀和調試JVM代碼。這里簡單介紹了G1的代碼架構和組織形式。另外簡單介紹了Linux的調試工具GDB,這個工具對于想要了解JVM細節的同學必不可少。
·附錄B介紹如何使用NMT對JVM內存進行跟蹤和調試。這個知識對于想要深入理解JVM內存的管理非常有幫助,另外在實際工作中,特別是JDK升級中我們必須比較同一應用在不同JVM運行情況下的內存使用。
·附錄C介紹了Java程序員閱讀JVM時需要知道的一些C++知識。這里并未羅列C++的語法以及語法特性,僅僅介紹一些C++語言特有的、而Java語言沒有的語法,或者Java語言中的使用或理解不同于C++語言的部分語法。這個知識是為Java程序員準備的,特別是為在閱讀JVM代碼時準備的。
G1在JDK 6中出現,經歷JDK 7的發展,到JDK 8已經相當成熟,在JDK 9之后G1就作為JVM的默認垃圾回收器。JDK 8作為Oracle公司長期支持的版本,本書主要基于JDK 8進行分析,所用的版本是jdk8u60。在第7章中為了擴展讀者的視野,追蹤最新的技術,還介紹了JDK 10中的并行Full GC。讀者可以自行到OpenJDK的官網下載,也可以使用筆者在GitHub中的備份(JDK 8:https://github.com/chenghanpeng/jdk8u60,JDK 10:https://github.com/chenghanpeng/jdk10u)。
本書在分析源碼的時候會給出源代碼所屬的文件,例如在介紹G1分區類型時,指出源代碼位于hotspot/src/share/vm/gc_implementation/g1/heapRegionType.hpp,這里的hotspot就是你下載的jdk8u60代碼里面的一級目錄。如果你不希望在本地保留源代碼可以直接瀏覽網址https://github.com/chenghanpeng/jdk8u60,在此你可以找到這個一級目錄hotspot,然后通過逐個查看子目錄src、share、vm、gc_implementation、g1就可以找到源文件heapRegionType.hpp。
需要注意的是,在分析源碼的時候為了節約篇幅,通常會對原始的代碼進行一些調整,例如刪除一些大括號、統計信息、打印信息,或者刪除一些不影響理解原理和算法的代碼,大家在和源碼比較時需要注意這些變化。另外對于定義在header文件和cpp文件中的一些函數,為了使代碼緊湊,通常會忽略頭文件中的定義,直接按照C++的語法,即類名::成員函數的方式給出源碼,這樣的代碼可能和原文件不完全一致,但是完全符合C++語言的組織,閱讀源碼時要注意將定義和實現分開。
由于筆者水平有限,時間倉促,書中難免出現一些錯誤或者不準確的地方,懇請讀者批評指正。可以通過https://github.com/chenghanpeng/jdk8u60/issues進行討論,期待能夠得到讀者朋友們的真情反饋,在技術道路上互勉共進。
在本書的寫作過程中,得到了很多朋友以及同事的幫助和支持,在此表示衷心的感謝!
感謝吳怡編輯的支持和鼓勵,在寫作過程中給出了非常多的意見和建議,不厭其煩地認真和筆者溝通,力爭做到清晰、準確、無誤。感謝你的耐心,為你的專業精神致敬!
感謝我的家人,特別是謝謝我的兒子,體諒爸爸犧牲了陪伴你的時間。有了你們的支持和幫助,我才有時間和精力去完成寫作。
- Spring 5.0 Microservices(Second Edition)
- .NET之美:.NET關鍵技術深入解析
- Apache Oozie Essentials
- 解構產品經理:互聯網產品策劃入門寶典
- 自己動手實現Lua:虛擬機、編譯器和標準庫
- PaaS程序設計
- Dependency Injection in .NET Core 2.0
- R語言數據可視化實戰
- Scala編程實戰(原書第2版)
- Mathematica Data Analysis
- H5頁面設計:Mugeda版(微課版)
- SciPy Recipes
- 零代碼實戰:企業級應用搭建與案例詳解
- 從Power BI到Analysis Services:企業級數據分析實戰
- Machine Learning for Developers