G1垃圾收集器簡述

G1垃圾收集器簡述

全文共兩部分,有基礎的讀者只須要閱讀第一部分"G1垃圾收集器在最新幾個版本的發展",第二部分爲基礎部分.java

  • G1垃圾收集器在最新幾個版本的發展

    G1垃圾收集器始見於1.7版本,在後續的幾個版本中對它進行了優化和改進:在JAVA9中,G1垃圾收集器增長了幾個可配置選項的自動發現功能,同時它被設置爲默認的垃圾收集器(32/64位服務器),取代了Parallel gc,同時deprecated了cms;在JAVA10中,G1的FULL GC被改進爲並行以縮短期;在JAVA11中,g1在處理Reference時的線程數支持自適應調整,也是同時在這一版,全部gc在stw階段均支持了自適應的並行度調整.
    截止此文時,最新版的JAVA12在推出Shenandoah GC,對zgc支持併發類卸載的同時,依舊對G1垃圾收集器進行了幾點優化:web

  1. 對G1 和 Parallel GC推出的體驗特性,支持NV-DIMM等可選設備上分配老年代(自JAVA10開始,堆內存能夠分配在NV-DIMM,關於DIMM的優勢本文不做論述).

    若是啓用了這個功能,年輕代依舊使用DRAM安放,僅有老年代會存放在NV-DIMM.G1在任何一個給定的時間點保證提交到DRAM和NV-DIMM上的內存永遠會小於-Xms指定的內存總量.目前最新的實現方式是將完整的JAVA堆預分配到NV-DIMM文件系統,這樣能夠避免動態的代擴容,可是將保證NV-DIMM文件系統空間充足的責任甩給了用戶.啓用時,即便用戶顯式設置了年輕代大小,虛擬機同時也基於DRAM的總可用量對年輕代進行了限定.
    舉例說明:若是虛擬機在一個具有32G的DRAM和1024G的NV-DIMM內存的系統上運行,指定了-Xms756g,虛擬機會對年輕代進行計算,並使用計算結果進行限定.
    若是未指定-XX:MaxNewSize或-Xmn,最大年輕代大小將設置爲可用內存的百分之八十(25.6G);指定了-XX:MaxNewSize 或 -Xmn,最大年輕代大小依舊以25.6G爲封頂;使用-XX:MaxRAM可告訴虛擬機有多少DRAM可用,那麼年輕代的大小設置爲該參數指定的值的百分之八十;使用-XX:MaxRAMPercentage可指定DRAM中有多大的百分比對於年輕代可用(默認百分之八十);
    啓動時,可經過日誌選項gc+ergo=info打印最大年輕代大小.算法

  2. g1可在併發標記週期釋放內存.

    在12版,G1默承認以在併發標記週期將應用進程不須要的空閒的堆內存交回操做系統,從而提高了java進程對內存的使用效率,若使用-Xms選項將初始內存設置爲最大內存,則此功能會被禁用.tomcat

  3. 可終止的g1混合gc

    G1有一個很是重要的目標:在gc停頓階段適配用戶指望的停頓時間.一直以來,G1選擇一段收集期內完成的大量工做信息(必定程度上依賴於應用行爲自己)做爲樣本進行高度分析,分析後選定的一組分區被稱爲collection set(簡稱cs,即回收集),一旦cs被選定而且G1開始了回收,那麼G1必須不停頓地回收全部這些cs中的存活對象.這個行爲可能會由於G1的啓發式算法選擇了過大的cs而致使回收時間超過用戶設置的目標停頓時間.應用行爲突變是一個典型的復現場景,它會形成啓發式算法依託於"髒"數據,當出現這種狀況時,能夠觀測到mix gc(關於mix gc可參考後面更基礎的描述)過程當中包含了過多的老年代分區,所以須要一個機制來發現G1的啓發式算法是否重複選擇了前面垃圾收集過程當中的錯誤工做數據,並在必要時讓G1增量地按小步運行回收工做,每個小步完成以後,回收工做均可以取消,經過這樣的機制,G1可更加容易地達到或者接近用戶指定的目標停頓時間要求.
    具體的過程:若是G1發現了啓發式算法重複選取了錯誤的分區數,當即切換爲一個更加精細的mix gc方式,首先,將cs分割成兩個部分,必選和可選.必選部分會包含cs中的g1不能細化處理的部分(如年輕代),但爲了提高效率,它也能夠包含部分老年代分區.剩餘的老年代分區便組成了可選的cs部分.
    當G1完成了必選部分的回收以後,若是有時間剩餘,G1以更細粒度回收可選部分.回收cs可選部分的粒度取決於這個剩餘時間,粒度最細化的狀況下限定在一個分區.在完成可選cs的任何一部分回收後,G1能夠依照剩餘時間來決定是否取消回收過程.
    由於粒度的細化,G1對爲達到停頓時間目標而預算的cs變的更加精確,可選的cs會在整個過程當中愈來愈小,最終結果是必選部分再一次包含了cs的全部分區.若是某一刻啓發式算法的結果變的從新不精確起來,那麼下一次回收將會從新包含"必選"和"可選".服務器

  4. 讓g1在空閒時自動釋放已提交但未使用的內存.數據結構

    在此以前,G1不會定時地將堆中已提交的內存釋放回操做系統,它只會在full gc或併發週期內作這件事,由於g1一直在努力避免full gc,並僅會基於java堆的佔用和內存分配活動來觸發一個併發週期,G1除非顯式強制要求,不然不會將堆內存釋放.這個行爲在付費購買資源的容器化環境中是明顯的劣勢.即便在空閒時,虛擬機使用破碎的內存資源時,G1也會持有所有的java堆,結果就是雲用戶爲這些空閒佔用的資源進行了額外的買單.
    若是讓虛擬機有能力發現處於空閒態的java堆,自動在空閒時減小堆的使用,將會是一個大幅提高.固然,Shenandoah 和 GenCon 收集器已經支持了相似的功能.顯然的,對於web服務用戶來講,夜間的請求數量和白天的請求數量每每相差甚遠,服務器在白天頻繁地處理請求,而大部分夜晚卻處於空閒狀態.固然官方仍是作出了相應的調研,經過對實時的tomcat服務器的晝夜服務差距估計,此解決方案能夠減小虛擬機的85%的內存提交量.
    爲了實現儘量將無用內存釋放回操做系統的目標,G1將會在應用空閒時週期的嘗試觸發一個併發週期,這用以判定java堆的全局使用結果,它將會致使java堆中的未使用部分自動地返回給操做系統,固然用戶能夠選擇在這一個過程當中使用full gc來最大化返回內存.有兩種狀況,G1會認爲應用不活躍並觸發週期gc:第一是任何一次gc停斷後超過"G1PeriodicGCInterval"指定的毫秒數且這期間沒有任何正在進行的併發週期.若是該值指定爲0,則表示功能禁用;第二是由JVM調用宿主系統的getloadavg()方法返回的一分鐘平均系統負載值低於"G1PeriodicGCSystemLoadThreshold"時,但若是指定G1PeriodicGCSystemLoadThreshold爲0也會禁用.若是兩個條件均不知足,則取消相應的週期gc,直到下一次G1PeriodicGCInterval知足了爲止.週期gc的類型是由選項G1PeriodicGCInvokesConcurrent決定的,若是設置了該選項,G1會開啓一個併發週期來回收,不然會使用full GC.在每一次回收後G1都會調整當前的堆大小,悄悄地把內存還給操做系統.新的java堆大小是由一些配置決定的,包含但不限於MinHeapFreeRatio,MaxHeapFreeRatio,最小最大堆大小配置.
    爲了不擾動應用進程,G1默認會在週期gc中間開始或繼續一個併發週期,可是相對於full gc,明顯不能釋放更多的內存.
    在相應的gc日誌中,由這個機制觸發的gc將會打上相應的標籤,詳見下面的例子.
    (1) 6.084s[gc,periodic ] Checking for periodic GC.併發

    [6.086s][info ][gc          ] GC(13) Pause Young (Concurrent Start) (G1 Periodic Collection) 37M->36M(78M) 1.786ms

    (2) 9.087s[gc,periodic ] Checking for periodic GC.佈局

    [9.088s][info ][gc          ] GC(15) Pause Young (Prepare Mixed) (G1 Periodic Collection) 9M->9M(32M) 0.722ms

    (3) 12.089s[gc,periodic ] Checking for periodic GC.學習

    [12.091s][info ][gc          ] GC(16) Pause Young (Mixed) (G1 Periodic Collection) 9M->5M(32M) 1.776ms

    (4) 15.092s[gc,periodic ] Checking for periodic GC.優化

    [15.097s][info ][gc          ] GC(17) Pause Young (Mixed) (G1 Periodic Collection) 5M->1M(32M) 4.142ms

    (5) 18.098s[gc,periodic ] Checking for periodic GC.

    [18.100s][info ][gc          ] GC(18) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 1.685ms

    (6) 21.101s[gc,periodic ] Checking for periodic GC.

    [21.102s][info ][gc          ] GC(20) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.868ms

    (7) 24.104s[gc,periodic ] Checking for periodic GC.

    [24.104s][info ][gc          ] GC(22) Pause Young (Concurrent Start) (G1 Periodic Collection) 1M->1M(32M) 0.778ms

    上面的例子指定了G1PeriodicGCInterval的值爲3000ms,在(1)中應用保持不活躍一段時間後,G1啓動了一個併發週期,標記(Concurrent Start)和(G1 Periodic Collection).此次併發週期的啓動當即釋放了一些內存,能夠看到(1)到(2)的(78M)和(32M).在(2)到(4)觸發了更多的週期回收,這一次觸發的回收方式爲mix gc並整理堆.緊隨的週期gc(5)到(7)僅開始了一個併發週期,由於G1的策略判斷此時老年代的垃圾數量不足,沒必要開始mix gc.本例中,由於堆大小已經保持到最小堆size,週期gc(5)到(7)不會進一步壓縮堆內存.
    在應用閒暇時間,對象存活狀態的改變(如軟引用過時)可能會觸發已提交java堆內存的進一步縮小.

  • G1垃圾收集器簡介

    本節的一些數量指標主要依託於最新JAVA12的文檔,有一些"量化"的數值可能與其餘文章不一致.
    按照官方文檔說法,"垃圾優先"(G1 即garbage first)收集器專一於多處理器,大內存的應用場景.(這個大內存彷佛是在zgc等專一超大堆gc出現以前的描述)它試圖在少許配置前提下,知足用戶指定的目標停頓時間,同時保持必定的高吞吐量.G1旨在應用延遲和吞吐量之間提供一個當前應用環境下的最佳平衡,這些典型適選G1的應用環境的特徵有:
    堆大小高達數十G或者更大(在一些文章和問答中有6G的標識,可能爲舊版本),且存活數據可佔用高達50%的堆內存.
    對象分配和晉升的速度可能會隨着時間推移大幅度改變.
    堆中有大量的內存碎片.
    指望不長於幾百毫秒的可預測的停頓時間目標,避免長期的gc停頓.
    G1已替換了cms垃圾收集器且是默認的垃圾收集器.

  1. 基本概念

    按照官方表述,G1是一個分代的,增變的,並行的,多數狀況併發的,會stop-the-world的,"排泄/遷移"(evacuating)的垃圾收集器,專一於每次stop-the-world的停頓的停頓時間.這個"分代"實際上是可選的,即也有無代模式."增變"特性在前面的新特性章節已經闡述過.與其餘垃圾收集器相似,G1也將堆劃分(虛擬的)爲年輕代和老年代,一樣符合老規矩:年輕代的回收是最高效的,也是主要的工做,偶爾也伴隨一些老年代的空間回收.
    在G1中,爲了提高吞吐量,有一些操做永遠是stop-the-world的.其餘的一些要長期的,如全局標記這種要全堆進行的操做與應用程序併發進行.爲了讓空間回收的stop-the-world停頓儘量減小,G1並行的分步的遞增進行空間回收.G1經過追蹤此前應用行爲和垃圾回收停頓的信息來構建一個與開銷有關的模型.它使用這些信息去圍限停頓期間可作的工做.舉個例子,G1首先回收最高效的區域(也即垃圾最滿的區域,所以稱爲垃圾-優先).
    G1使用"排泄"的方式回收大多空間,在選定的內存區域,存活的對象被拷貝到新的區域,並在這個過程對他們進行壓縮整理.在排泄完成後,存活對象以前佔用的空間能夠交給應用程序從新使用, 便可用來分配新的對象.

  2. 堆佈局
    G1將堆分割成一組等大小的堆區,一個區是內存分配和回收的基本單元.在任何一個給定的時間,每個區能夠是空的(下圖淺灰色),也能夠分配了特殊的代(年輕或老年).當有內存分配請求到來時,內存管理者上交空閒的分區,把它們指派給一個代而且交給應用程序,做爲應用程序可自由分配的自由空間.
    堆佈局
    上圖中,純紅色爲eden區,標有'S'的爲倖存者區,與此前其餘的垃圾收集器功能保持一致,不一樣之處在於G1中這些同代的分區自身並不連續.老年代分區由淺藍色表示,它的佔用者可能會是跨多個分區的大型對象(帶有'H'),通常狀況下,應用程序會將對象分配到年輕代中的eden區,大對象則直接分配到老年代.
  3. gc週期

    G1垃圾收集器宏觀上有兩個階段,它會在兩個階段之間往復切換.兩個階段分別是young-only階段和空間回收階段.young-only階段包含一系列逐漸充滿老年代可用空間的gc.空間回收階段,G1遞進地回收老年代中的空間,同時也處理年輕代.緊接着,G1又從新進入young-only階段,開始新一輪的循環.
    G1週期
    上圖表示G1的不一樣階段以及有關的停頓.能夠看到圖上有一些實心的圓圈,每個圈都表示一次gc停頓:藍色圓表示young-only回收停頓,橘黃色表示包含標記過程的停頓,紅色表示混合gc的停頓.這些停頓用箭頭標記了循環順序,從young-only進入到混合gc,再回到young-only.young-only階段開始於若干個young-only gc,圖上由小藍圈表示,在幾回gc後,老年代中的對象佔有超過了InitiatingHeapOccupancyPercent定義的閾值,則下一次的gc停頓將會初始化一個標記gc的停頓,上圖用大藍圈表示,它除了標記之外,其餘的工做與young-only停頓一致,同時它會爲併發標記作出準備.
    當運行併發標記時,其餘young only停頓也可能會發生,直到remark停頓(第一個大黃圈)爲止(做者認爲remark等階段不該簡單地按字面意思翻譯,如直譯爲從新標記,可是這一階段作出的工做自己也不止如此,就如gc本意garbage collection/collector是垃圾回收/器的意思,卻不止負責回收,也影響內存分配等,其餘的階段也相似),在remark階段,G1完成標記.直到Cleanup階段以前,仍舊可能會有額外的young-only gc.在Cleanup停頓以後,將會有一個最終的young-only gc終止整個young-only階段.在空間回收階段,會發生一系列的混合gc,上圖中用紅色圈表示,通常來說,它的數量會比young-only階段的young-only停頓少,由於G1努力去使空間回收儘量的高效.下面總結G1週期中的各個階段,階段中的停頓和轉換:
    young-only階段:這一階段開始於幾個普通的young gc,它們會提高年輕代對象到老年代.young only階段與空間回收階段的轉換會在老年代的佔有達到一個肯定閾值後開始,這個閾值爲初始堆佔有閾(Initiating Heap Occupancy threshold),在此時刻,G1會調度一個併發開始的young gc並用它替換普通的young gc.
    併發開始:它除了執行普通的young gc外,還開始了標記過程,它會併發地標記全部老年代中當前可達的存活對象以用於後續的空間回收階段.當標記未完成時,普通young gc可能也會穿插發生.標記完成伴隨兩個特殊的停頓:Remark和Cleanup.
    remark:此停頓會終止自身的標記過程,它執行全局引用處理和類卸載,以及回收徹底空的分區和清空內部數據結構.在remark和cleanup兩階段中間,G1會併發計算後續可回收的選定的老年代空間信息,此信息會最終在cleanup階段肯定.
    cleanup:這一次停頓會決定是否會有一個緊隨其後的空間回收階段,若是有空間回收階段發生,那麼young-only階段會在最後爲混合gc完成準備.
    空間回收階段:這一階段包含多個混合gc,除了回收年輕代,也會排泄老年代存活對象.這一階段會持續一段時間,直到G1發現排泄更多的老年代分區也不會獲得與開銷等同價值的空閒空間的回報爲止.
    在空間回收階段以後,gc循環重啓,開始新一輪的young-only階段,若是應用在收集對象存活信息過程當中就已耗盡內存,那麼G1如其餘垃圾收集器同樣,執行備選的full gc.

  4. G1停頓與cs,分代大小

    G1在stop-the-world停頓中執行空間回收,存活的對象會被從源區拷貝到目標區,存活對象的引用也會被相應地調整到新地址.
    對於非巨型對象,一個對象的目標堆區決定於一些特定規則,若源對象是年輕代對象(eden區或倖存者區),則根據它們的年齡決定拷貝到倖存者區或老年代.
    老年代對象拷貝到其餘老年代.大對象則區別對待,G1只決定它們的存活與否,若是它們被斷定爲非存活對象,則就地回收,G1不會移動巨型對象.
    受gc類型影響,cs可能包含不一樣種的分區.在young only階段,cs只包含年輕代和可能被回收的潛在巨型對象區;在空間回收階段,cs包含年輕代,可能被回收的潛在巨型對象區,一些老年代候選區.
    G1會在併發週期中選定候選回收分區,在remark停頓期間,G1選定那些較低佔有率的,也就是包含大量空餘空間的分區,這些分區會接下來在remark和cleanup之間進行後面的收集,cleanup停頓會根據回收它們的效率進行排序,回收效率高的分區,即看起來回收花費時間少,包含更多空閒空間的會在後續的混合gc中優先使用.
    這一段話摘自官網,官方說了"that contain more free space are preferred in subsequent mixed collections",可能會有很多看客看暈(包含做者本身),G1不是"垃圾優先"嗎?它應該優先選取垃圾最多的分區去回收,做者在stackoverflow上找到了老外對此的解釋,看來有此疑問的不止做者一人.根據stackoverflow上的解釋,此處"包含更多的空閒空間"其實就是指包含更多的垃圾,這多是英語漢語之間的一個美妙誤會吧.
    Why G1 Garbage collector is called Garbage First
    所謂"mostly empty"
    從上圖可見,"mostly empty"是指包含儘量多的"可回收的垃圾".貼子做者也指出,對於要回收的分區,包含最大數量的可回收空間至少有兩點好處:一是能夠儘快的得到最多的空間,二是對於G1這種使用拷貝(標記清除整理)的收集器,源分區存活對象越少,須要作的copy工做就越少,就能夠越高效地回收最多的空間.
    提到空間的回收以及cs的挑選,順便提一提比較簡單的堆空間大小調整問題.G1遵照調整java堆大小的標準規則,如-XX:InitialHeapSize能夠指定java堆的最小大小,使用-XX:MaxHeapSize則指定了java堆的最大大小,-XX:MinHeapFreeRatio指定最小可用內存的百分比,-XX:MaxHeapFreeRatio指定調整堆大小後最大空閒內存佔比.G1垃圾收集器僅會在remark和full gc的停頓期間調整堆的容量.這一過程會從操做系統獲取內存或釋放內存.
    G1在每一次普通的young gc後都會爲下一個增變器階段調全年輕代的大小.經過這種方式,G1能夠依託於長期觀測實際的停頓時間來儘量適配-XX:MaxGCPauseTimeMillis和-XX:PauseTimeIntervalMillis 指定的停頓時間.它會考慮相近大小的年輕代排泄會花費多少時間.這會包括,多少對象會在回收過程當中copy,以及這些對象彼此間是怎麼聯繫的.
    若是未加其餘約束,G1會經過在-XX:G1NewSizePercent和-XX:G1MaxNewSizePercent之間(或者用-XX:NewSize和-XX:MaxNewSize)靈活自適應地調全年輕代大小的方式來儘量達到用戶設定的停頓時間標準.
    空間回收階段也伴隨代大小調整.在此階段,G1試圖去最大化一次gc停頓中能回收的老年代空間量,此時會將年輕代設置爲最小容許的大小,通常這個參數由-XX:G1NewSizePercent決定.在每個混合gc開始時,G1會從候選cs中選出一組加入到cs中,這些附加的老年代分區包含三個部分:
    第一部分是在排泄階段要保證的最小老年代分區集,這組老年代分區是由候選cs的分區數除以空間回收階段的長度決定.(空間回收階段長度由參數-XX:G1MixedGCCountTarget設置)若是G1預測在回收最小候選cs後還會有時間剩餘,將會從候選cs添加其餘老年代分區,直到耗費剩餘時間的80%.
    第三部分爲一組可選的cs分區,若是G1在本次停頓中遞增地完成了另外兩個部分的排泄後仍有時間剩餘,則添加這組可選的分區.前兩個分區集會在初始化收集過程當中回收,可選cs中的分區則會在剩餘停頓時間回收,經過這種方式實現了保證空間回收過程的同時提高了保證停頓時間和開銷最小的可能性.
    當候選cs中可回收的空間數量少於-XX:G1HeapWastePercent時,空間回收階段中止.
    前面說過,最新幾版的JAVA對G1作出了很多改進,其中一個改進就是週期gc,當因應用空閒而使得長久沒有gc時,虛擬機可能會持有大量的空閒內存,而這些空閒內存不能用在其餘地方.爲了不這種浪費,G1能夠強制規律性的gc,這須要提供-XX:G1PeriodicGCInterval選項,它將決定G1考慮執行一次gc的最小間隔時間,以毫秒爲單位.若是從以前的gc停頓起過去了這些時間,且沒有任何過程當中的併發週期,則G1會觸發額外的gc,此次觸發有不一樣的效果.
    在young only階段,G1會根據是否指定-XX:-G1PeriodicGCInvokesConcurrent來決定使用哪種停頓開始一個併發標記停頓,若是未指定,則用"併發標記停頓"來開始,不然使用full gc.在空間回收階段,G1會繼續觸發了適合當前過過程的停頓類型的空間回收階段.
    可以使用選項-XX:G1PeriodicGCSystemLoadThreshold來細化是否觸發一個gc,若是JVM宿主機調用getloadavg()返回的值(一分鐘平均負載)超過了該值,則不會有周期gc運行.

  5. 初始堆佔用

    初始堆佔用比(IHOP)是一個閾值,它表示老年代佔用的比例達到該值時觸發初始標記(前面說過,G1中初始標記同時於併發開始,併發開始包含普通gc和併發標記).g1默認自動地觀察標記週期中老年代對象在必定時間分配的對象狀況來判定最優的IHOP,也就是自適應的IHOP.若是啓動了這個功能,而且當前沒有足夠的觀測數據可決定一個良好的IHOP時,使用-XX:InitiatingHeapOccupancyPercent 設置的值做爲IHOP.使用-XX:-G1UseAdaptiveIHOP能夠關掉自適應IHOP,則G1將永遠使用-XX:InitiatingHeapOccupancyPercent做爲默認閾值.
    當沒有指定IHOP時,使用自適應的IHOP將會嘗試爲它準備一個初值,這個初始老年代佔用閾值默認爲當前最大老年代空間減去-XX:G1HeapReservePercent(也被稱爲extra buffer).

  6. 標記

    G1的標記過程使用開始快照(SATB)算法.它會在初始化標記停頓時提取當前虛擬機堆快照,全部此時存活的對象和後續分配的對象都會被在標記的剩餘過程當中被看成存活(後者默認標記不須要追蹤).因此在空間回收階段,會對那些在標記期間死亡的對象進行冗餘的工做(若是有異常發生),可是SATB算法減小了remark階段的停頓.好在這些被保守地看成存活的死亡對象會在下一次標記過程當中識別.

  7. 堆緊張時行爲

    當應用持續向內存中分配對象,致使沒有足夠的空間copy時,可能會致使排泄過程的失敗.排泄失敗意味着G1將試着去就地完成當前的gc(已經移動到新的位置或將來的及移動到新位置),此時將不會再移動未移動的對象,只會將對象間的一些引用關係進行調整.排泄失敗可能會帶來一些額外開銷,但通常狀況下應當和年輕代gc同一速度.在這一次gc排泄失敗以後,G1將會假定應用如常,至關於假定排泄失敗發生在gc的末尾,也就是大多對象已移動的狀況,有足夠的空間保證應用繼續運行,能完成標記和開始空間回收階段.
    若是這個假定不能保持,那麼G1只能進行full gc,這將會進行就地壓縮整理,會是一個很是緩慢的過程.
  8. 大對象行爲

    大對象是指大於或等於半個分區大小的對象.當前分區大小能夠用-XX:G1HeapRegionSize 選項來設置.
    這些大對象有時會被特殊對待,每一個大對象會在老年代分區中連續分配.對象的開始點老是在該分區組中的第一個分區的起始,最後一個分區的剩餘部分將不會在對象分配中使用,直到整個對象回收爲止.
    通常狀況下,大對象只能夠在標記過程的末尾的cleanup停頓期間進行回收,或者在它不可達後的full gc中回收.但對於一些特殊的大型對象,如全部元素均爲基本類型,G1會在任何gc停頓過程當中嘗試碰運氣回收它們.這個特性默認開啓,可使用選項-XX:G1EagerReclaimHumongousObjects 關閉.

    大對象的分配可能會致使gc停頓過早地發生.G1會在任何一個大對象分配時檢查初始堆佔用比(IHOP),這可能會強制當即開始young gc的初始標記.大對象即便在full gc中也從不移動,也可能會致使full gc過程緩慢或者雖然存在大量空閒空間卻因大量的內存碎片而出人意料的oom.
  9. 對比其餘gc

    與其餘gc對比,簡單列舉區別以下:
    Parallel gc也會整理和回收老年代空間,但只能做爲一個總體進行.G1至關於遞進地,增量地用多個更短的gc過程完成了一樣的工做.這樣減小了停頓時間,也消費了一些吞吐量.
    與cms類似,G1併發完成老年代的空間回收,然而cms不能解決老年代堆的碎片化問題,最終只能致使長運行的full gc.
    G1可能比上述垃圾收集器開銷更大,由於它的併發特性而影響到了吞吐量.
    ZGC的目標在於超大堆,追求更短的停頓時間,卻更大的消費了吞吐量.
    取決於G1的工做方式,它有一些獨有的機制來提高gc效率.如G1能夠在任何collection過程當中回收徹底空的,大的老年代分區,這能夠避免不少其餘沒必要要的gc,不太費力的釋放大量空間.G1也能夠嘗試併發地將堆內存中的重複字符串去重.
    回收老年代中空的,巨型對象默認開啓,可以使用選項-XX:-G1EagerReclaimHumongousObjects開啓,字符串去重默認關閉,可以使用-XX:+G1EnableStringDeduplication開啓.

書寫心得

到此G1垃圾收集器簡述就寫完了,主要參考資料爲官方的幾篇文章和文檔,未涉及複雜高深的內部實現,最近幾個版本的java都在不停地優化,固然gc只是其中一小部分,大到即時編譯和gc,小到線程握手和引入nests等,感受java愈來愈重視"內功"了.
近幾版除了G1進行了改良之外,官方也推出了幾種新的垃圾收集器:
Epsilon GC是jdk11推出的無操做(no-op)垃圾收集器,方便於咱們單獨觀測一個應用的內存分配狀況,不受回收干擾.
zgc是一個旨在超大堆下保持低延遲的回收器,據官方wiki,在jdk13中已經將支持的最大堆從4T提高到了16T,回收時間不受堆內存大小的影響,相應的着色指針技術,讀屏障技術簡直讓人歎爲觀止,它在jdk11中開放體驗版,但不支持類卸載,jdk12中補上了這一功能.
Shenandoah 也是一個超大堆低延遲的回收器,jdk12中開放,不一樣於zgc的是它使用了"間接指針"的技術實現對象拷貝過程的併發進行.
總之,發自心裏地佩服這些做者,我輩學習之楷模.
相關文章
相關標籤/搜索