Java程序不用像C++程序在程序中自行處理內存的回收釋放。這是由於Java在JVM虛擬機上增長了垃圾回收(GC)機制,用以在合適的時間觸發垃圾回收.html
引用計數法, 根搜索法, 標記-清除算法, 複製算法, 標記-壓縮算法, 分代收集算法java
當引用了某個對象A, A的引用計數器就加1; 當一個引用失效時, 就減1.git
只要對象A的引用變爲0, 就說明對象A就不可能再被使用(由於沒有引用了啊)., 就能夠被回收了.github
可是如此的引用計數法, 沒法解決循環引用的問題: A引用B, B引用A, 沒有其餘的有效引用指向A, 也沒有其餘的有效引用指向B, 沒有有效引用就應當被回收了, 可是此時計數器都是1, 不等於0, 不會被斷定爲垃圾, 就形成了AB都沒法被回收的問題.web
大多數JVM採用的是根搜索算法. 這種算法以根對象集合爲起始點, 開始遍歷可達性. 可達的對象纔是存活的對象.算法
Java棧內的對象引用, 本地方法棧內的對象引用, 運行時常量池中的對象引用, 方法區中類靜態屬性的對象引用, 一個類對應一個數據類型的Class對象.segmentfault
若是重寫了finalize()方法, 那麼仍是能夠復活一次的.數據結構
覆蓋了finalize()後由於會被放入到F-Queue中, 由一個低優先級的Finalizer線程來執行, 緩慢地進行清除.多線程
若是在清除到該對象前, 該對象逃脫了(與引用鏈上的對象產生了聯繫), 那麼就復活了.併發
可是隻能復活一次, 下一次就不會再執行finalize()了, 也就不會再復活了.
public class TestGC { private static TestGC instance=null; private static void isAlive(){ if(instance == null){ System.out.println("我被回收了"); }else{ System.out.println("我還活着"); } } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize 方法執行完了"); instance=this; } public static void main(String [] args) throws InterruptedException { System.out.println("新建一個實例"); instance=new TestGC(); instance=null; System.gc(); System.out.println("\ngc"); Thread.sleep(1000); System.out.println("1秒後:"); isAlive(); instance=null; System.gc(); System.out.println("\ngc"); isAlive(); } }
先標記, 再清除. 缺點就是會產生空間碎片
預留另外一塊空間, 將存活的對象複製過去, 而後將原先的全部內容清空便可.
標記後進行壓縮.
優勢是解決了標記-整理的空間碎片問題, 解決了複製算法的須要預留一塊一樣大小的空間的問題.
缺點是, 爲了實現壓縮, 須要對堆內容進行更屢次數的遍歷.
標記壓縮算法的實現有: Two-Finger 算法, LISP2 算法
將對象根據對象的生命週期進行分類. 分爲年輕代, 老年代, 持久代. 對不一樣生命週期的對象使用不一樣的算法進行垃圾回收.
Serial/Serial Old收集器, ParNew收集器, Parallel/Parallel Old收集器, CMS收集器, G1收集器
1. 做用於年輕代.
2. Client模式下的默認垃圾收集器.
3. 採用複製算法.
4. 串行回收.
5. -XX:+UseSerialGC (年輕代串行, 老年代串行)
6. 適用於小存儲器, 少CPU
1. 做用於老年代.
2. 標記-壓縮算法.
3. 串行回收.
4. -XX:+UseSerialGC (年輕代串行, 老年代串行)
5. -XX:+UseParNewGC (年輕代並行, 老年代串行)
6. 適用於小存儲器, 少CPU
1. 多線程版的Serial.
2. -XX:+UseParNewGC (年輕代並行, 老年代默認串行)
3. -XX:+UseParallelGC (年輕代並行, 老年代默認串行)
1. 與ParNew不一樣, 能夠控制吞吐量, 經過-XX:+GCTimeRatio, 設置內存回收的時間所佔JVM運行總時間 的比例.公式爲: 1/(1+N), 默認N爲99, 即默認1%的時間用於垃圾回收.
2. -XX:+MaxGCPauseMills , 用於設置執行內存回收時
3. -XX:+UseAdaptiveSizePolicy, 自適應GC策略. 包括年輕代的大小, Eden和Survivor的比例, 晉升老年代的對象年齡, 吞吐量和停頓時間 等.
4. -XX:ParallelGCThreads能夠用於設置垃圾回收時的線程數量.
1. -XX:+UseParallelGC: 年輕代使用並行回收收集器, 老年代使用串行收集器
2. -XX:+UseParallelOldGC: 年輕代和老年代都是使用並行回收收集器
3. 採用複製算法.
1. -XX:+UseParallelOldGC: 年輕代和老年代都是使用並行回收收集器
2. 採用標記壓縮算法.
1. CMS全稱是Concurrent-Mark-Sweep.
2. 標記清除算法
3. 對年老代進行垃圾收集
4. -XX:+UseConcMarkSweepGC, 使用CMS收集器執行內存回收任務.
5. 過程: (1)Stop the world: 這個階段會標記出可達對象,而後恢復被暫停的全部線程. (2)併發標記:而後併發地將不可達對象標記爲垃圾. (3)C再次標記階段: 程序會再次"stop the world", 以確保這些垃圾對象可以被成功且正確地標記. (因爲在併發標記垃圾對象的過程當中, 應用線程和垃圾收集線程在同時運行, 可能沒法確保以前被標記爲垃圾的無用對象都可以被成功且正確地被標記.)
6. 前面提到的幾個老年代垃圾回收器都是採用標記-壓縮算法, 而CMS採用的是標記-清除算法, 這意味着會有內存碎片產生, 擦用空閒列表(Free List)執行內存分配.
7. -XX:+UseCMS-CompactAtFullCollection, 用於指定在執行完Full GC後是否對內存空間進行壓縮整理.
8. -XX:CMSFullGCs-BeforeCompaction, 用於設置在執行多少次Full GC後堆內存空間進行壓縮整理.
9. 在併發標記階段若是產生新的垃圾對象, CMS將沒法對這些垃圾對象進行標記, 最終會致使這些新產生的垃圾對象沒有被及時回收, 從而只能在下一次執行GC時釋放這些以前未被回收的內存空間.
10. -XX:CMSInitiatingFraction, 用於設置當老年代中的內存使用率達到多少百分比的時候執行內存回收(低版本的JDK默認爲68%, JDK6以上版本默認值爲92%).這裏的內存範圍僅限於老年代, 而非整個堆空間, 所以能夠有效下降Full GC的次數.
11. "Promotion Failed" "Concurrent Mode Failure"時, 會觸發Full GC.
12. CMS默認啓動的線程數是: (年輕代並行收集器的線程數 + 3) / 4 , 可使用-XX:ParallelCMSThreads參數設定CMS的線程數量.
CMS收集器在老年代堆內存的回收中執行分爲如下階段:
階段 | 說明 |
---|---|
(1) 初始標記 (Initial Mark) | (Stop the World Event,全部應用線程暫停) 在老年代(old generation)中的對象, 若是從年輕代(young generation)中能訪問到, 則被 「標記,marked」 爲可達的(reachable).對象在舊一代「標誌」能夠包括這些對象可能能夠從年輕一代。暫停時間通常持續時間較短,相對小的收集暫停時間. |
(2) 併發標記 (Concurrent Marking) | 在Java應用程序線程運行的同時遍歷老年代(tenured generation)的可達對象圖。掃描從被標記的對象開始,直到遍歷完從root可達的全部對象. 調整器(mutators)在併發階段的二、三、5階段執行,在這些階段中新分配的全部對象(包括被提高的對象)都馬上標記爲存活狀態. |
(3) 再次標記(Remark) | (Stop the World Event, 全部應用線程暫停) 查找在併發標記階段漏過的對象,這些對象是在併發收集器完成對象跟蹤以後由應用線程更新的. |
(4) 併發清理(Concurrent Sweep) | 回收在標記階段(marking phases)肯定爲不可及的對象. 死對象的回收將此對象佔用的空間增長到一個空閒列表(free list),供之後的分配使用。死對象的合併可能在此時發生. 請注意,存活的對象並無被移動. |
(5) 重置(Resetting) | 清理數據結構,爲下一個併發收集作準備. |
堆內存被分爲3個空間.
年輕代(Young generation)分爲 1個新生代空間(Eden)和2個存活區(survivor spaces). 老年代(Old generation)是一大塊連續的空間, 垃圾回收(Object collection)就地解決(is done in place), 除了 Full GC, 不然不會進行壓縮(compaction).
年輕代(young generation)用高亮的綠色表示, 老年代(old generation)用藍色表示。若是程序運行了一段時間,那麼 CMS 看起來就像下圖這個樣子. 對象散落在老年代中的各處地方.
在使用 CMS 時, 老年代的對象回收就地進行(deallocated in place). 他們不會被移動到其餘地方. 除了 Full GC, 不然內存空間不會進行壓縮.
Eden區和survivor區中的存活對象被拷貝到另外一個空的survivor 區. 存活時間更長,達到閥值的對象會被提高到老年代(promoted to old generation).
年輕代(Young)進行一次垃圾回收以後, Eden 區被清理乾淨(cleared),兩個 survivor 區中的一個也被清理乾淨了. 以下圖所示:
圖中新提高的對象用深藍色來標識. 綠色的部分是年輕代中存活的對象,但還沒被提高到老年代中.
兩次stop the world事件發生在: 初始標記(initial mark)以及從新標記(remark)階段. 當老年代達到必定的佔有率時,CMS垃圾回收器就開始工做.
(1) 初始標記(Initial mark)階段的停頓時間很短,在此階段存活的(live,reachable,可及的) 對象被記下來. (2) 併發標記(Concurrent marking)在程序繼續運行的同時找出存活的對象. 最後, 在第(3)階段(remark phase), 查找在第(2)階段(concurrent marking)中錯過的對象.
在前面階段未被標記的對象將會就地釋放(deallocated in place). 此處沒有壓縮(compaction).
備註: 未標記(Unmarked)的對象 == 已死對象(Dead Objects)
在第(4)步(Sweeping phase)以後, 能夠看到不少內存被釋放了. 還應該注意到,這裏並無執行內存壓縮整理(no compaction).
最後, CMS 收集器進入(move through)第(5)階段, 重置(resetting phase), 而後等候下一次的GC閥值到來(GC threshold).
G1垃圾收集器是在Java 7後纔可使用的特性,它的長遠目標是代替CMS收集器。G1收集器是一個並行的、併發的和增量式壓縮短暫停頓的垃圾收集器。G1收集器和其餘的收集器運行方式不同,不區分年輕代和年老代空間。它把堆空間劃分爲多個大小相等的區域(Region)。例如, 剛開始的時候Region A分配給了年輕代, 這個年輕代被回收結束後, 這個Region又被放回了空閒隊列中, 可能下一次就會被分配給老年代. 當進行垃圾收集時,它會優先收集存活對象較少的區域,所以叫「Garbage First」。
JVM參數 -XX:+UseG1GC
G1 GC的收集過程涵蓋4個階段: 年輕代GC, 併發標記週期, 混合收集, Full GC
1. 當Eden區間分配內存失敗(滿了)的時候, 一次年輕代回收就被觸發了. 此次觸發對於GC來講, 他的工做是釋放一些內存, 屬於一次輕量級的回收操做.
2. 當一個年輕代收集進行時, 整個年輕代會被回收, 全部的應用程序線程會被中斷, G1 GC會啓用多線程進行年輕代的回收.
3. 一次年輕代回收, 會讓存活的對象提高一次年齡. 當這個年齡到達必定的閾值, 就會被提高到老年區.
4. 每一次年輕代回收暫停期間, G1 GC從新調全年輕代的大小.
5. JVM會將每一個對象的年齡信息、各個年齡段對象的總大小記錄在「age table」表中。基於「age table」、survivor區大小、survivor區目標使用率(-XX:TargetSurvivorRatio, 默認50%
)、晉升年齡閾值(-XX:MaxTenuringThreshold, 默認15
),JVM會動態的計算tenuring threshold的值。一旦對象年齡達到了tenuring threshold就會晉升到老年代。
6. 爲何要動態的計算tenuring threshold的值呢?假設有不少年齡還未達到TenuringThreshold的對象依舊停留在survivor區,這樣不利於新對象從eden晉升到survivor。所以設置survivor區的目標使用率,當使用率達到時從新調整TenuringThreshold值,讓對象儘早的去old區。
7. 若是但願跟蹤每次新生代GC後,survivor區中對象的年齡分佈,可在啓動參數上增長-XX:+PrintTenuringDistribution
。
1. 不只僅對年輕代進行回收, 還須要進行一些老年代的回收.
2. -XX:InitiatingHeapOccupancyPercent (簡稱IHOP,默認值45), 當堆得佔有率達到IHOP閾值時, 初始化一個並行標記循環, 並啓動.
3. 並行標記循環包括如下幾個階段:
3. 在清洗階段, G1 GC根據每一個老年代區間的性能進行打分, 而後開始一次混合回收. 不只回收年輕代中的全部垃圾, 還回收垃圾較多的老年代區間.
成功完成併發標記週期後,G1 GC 從執行年輕代垃圾回收切換爲執行混合垃圾回收。在混合垃圾回收期間,G1 GC 能夠將一些舊的區域添加到 eden 和存活區供未來回收。所添加舊區域的確切數量由一系列標誌控制。G1 GC 回收了足夠的舊區域後(通過屢次混合垃圾回收),G1 將恢復執行年輕代垃圾回收,直到下一個標記週期完成。
G1提供了兩種GC模式,Young GC和Mixed GC,兩種都是徹底Stop The World的。
Young GC:選定全部年輕代裏的Region。經過控制年輕代的region個數,即年輕代內存大小,來控制young GC的時間開銷。
Mixed GC:選定全部年輕代裏的Region,外加根據global concurrent marking統計得出收集收益高的若干老年代Region。在用戶指定的開銷目標範圍內儘量選擇收益高的老年代Region。
由上面的描述可知,Mixed GC不是full GC,它只能回收部分老年代的Region,若是mixed GC實在沒法跟上程序分配內存的速度,致使老年代填滿沒法繼續進行Mixed GC,就會使用serial old GC(full GC)來收集整個GC heap。因此咱們能夠知道,G1是不提供full GC的。
上文中,屢次提到了global concurrent marking,它的執行過程相似CMS,可是不一樣的是,在G1 GC中,它主要是爲Mixed GC提供標記服務的,並非一次GC過程的一個必須環節。global concurrent marking的執行過程分爲四個步驟:
初始標記(initial mark,STW)。它標記了從GC Root開始直接可達的對象。
併發標記(Concurrent Marking)。這個階段從GC Root開始對heap中的對象標記,標記線程與應用程序線程並行執行,而且收集各個Region的存活對象信息。
最終標記(Remark,STW)。標記那些在併發標記階段發生變化的對象,將被回收。
清除垃圾(Cleanup)。清除空Region(沒有存活對象的),加入到free list。
第一階段initial mark是共用了Young GC的暫停,這是由於他們能夠複用root scan操做,因此能夠說global concurrent marking是伴隨Young GC而發生的。第四階段Cleanup只是回收了沒有存活對象的Region,因此它並不須要STW。
Young GC發生的時機你們都知道,那何時發生Mixed GC呢?實際上是由一些參數控制着的,另外也控制着哪些老年代Region會被選入CSet。
G1HeapWastePercent:在global concurrent marking結束以後,咱們能夠知道old gen regions中有多少空間要被回收,在每次YGC以後和再次發生Mixed GC以前,會檢查垃圾佔比是否達到此參數,只有達到了,下次纔會發生Mixed GC。
G1MixedGCLiveThresholdPercent:old generation region中的存活對象的佔比,只有在此參數之下,纔會被選入CSet。
G1MixedGCCountTarget:一次global concurrent marking以後,最多執行Mixed GC的次數。
G1OldCSetRegionThresholdPercent:一次Mixed GC中能被選入CSet的最多old generation region數量。
除了以上的參數,G1 GC相關的其餘主要的參數有:
參數 | 含義 |
---|---|
-XX:G1HeapRegionSize=n | 設置Region大小,並不是最終值 |
-XX:MaxGCPauseMillis | 設置G1收集過程目標時間,默認值200ms,不是硬性條件 |
-XX:G1NewSizePercent | 新生代最小值,默認值5% |
-XX:G1MaxNewSizePercent | 新生代最大值,默認值60% |
-XX:ParallelGCThreads | STW期間,並行GC線程數 |
-XX:ConcGCThreads=n | 併發標記階段,並行執行的線程數 |
-XX:InitiatingHeapOccupancyPercent | 設置觸發標記週期的 Java 堆佔用率閾值。默認值是45%。這裏的java堆佔比指的是non_young_capacity_bytes,包括old+humongous |
初始標記階段:在此階段,G1 GC 對根進行標記。該階段與常規的 (STW) 年輕代垃圾回收密切相關。
根區域掃描階段:G1 GC 在初始標記的存活區掃描對老年代的引用,並標記被引用的對象。該階段與應用程序(非 STW)同時運行,而且只有完成該階段後,才能開始下一次 STW 年輕代垃圾回收。
併發標記階段:G1 GC 在整個堆中查找可訪問的(存活的)對象。該階段與應用程序同時運行,能夠被 STW 年輕代垃圾回收中斷。
從新標記階段:該階段是 STW 回收,幫助完成標記週期。G1 GC 清空 SATB 緩衝區,跟蹤未被訪問的存活對象,並執行引用處理。
清理階段:在這個最後階段,G1 GC 執行統計和 RSet 淨化的 STW 操做。在統計期間,G1 GC 會識別徹底空閒的區域和可供進行混合垃圾回收的區域。清理階段在將空白區域重置並返回到空閒列表時爲部分併發。
三色標記算法
提到併發標記,咱們不得不瞭解併發標記的三色標記算法。它是描述追蹤式回收器的一種有用的方法,利用它能夠推演回收器的正確性。 首先,咱們將對象分紅三種類型的。
黑色:根對象,或者該對象與它的子對象都被掃描
灰色:對象自己被掃描,但還沒掃描完該對象中的子對象
白色:未被掃描對象,掃描完成全部對象以後,最終爲白色的爲不可達對象,即垃圾對象
當GC開始掃描對象時,按照以下圖步驟進行對象的掃描:
根對象被置爲黑色,子對象被置爲灰色。
繼續由灰色遍歷,將已掃描了子對象的對象置爲黑色。
遍歷了全部可達的對象後,全部可達的對象都變成了黑色。不可達的對象即爲白色,須要被清理。
這看起來很美好,可是若是在標記過程當中,應用程序也在運行,那麼對象的指針就有可能改變。這樣的話,咱們就會遇到一個問題:對象丟失問題
咱們看下面一種狀況,當垃圾收集器掃描到下面狀況時:
這時候應用程序執行了如下操做:
A.c=C
B.c=null
這樣,對象的狀態圖變成以下情形:
這時候垃圾收集器再標記掃描的時候就會下圖成這樣:
很顯然,此時C是白色,被認爲是垃圾須要清理掉,顯然這是不合理的。那麼咱們如何保證應用程序在運行的時候,GC標記的對象不丟失呢?有以下2中可行的方式:
恰好這對應CMS和G1的2種不一樣實現方式:
在CMS採用的是增量更新(Incremental update),只要在寫屏障(write barrier)裏發現要有一個白對象的引用被賦值到一個黑對象 的字段裏,那就把這個白對象變成灰色的。即插入的時候記錄下來。
在G1中,使用的是STAB(snapshot-at-the-beginning)的方式,刪除的時候記錄全部的對象,它有3個步驟:
1,在開始標記的時候生成一個快照圖標記存活對象
2,在併發標記的時候全部被改變的對象入隊(在write barrier裏把全部舊的引用所指向的對象都變成非白的)
3,可能存在遊離的垃圾,將在下次被收集
這樣,G1到如今能夠知道哪些老的分區可回收垃圾最多。 當全局併發標記完成後,在某個時刻,就開始了Mix GC。這些垃圾回收被稱做「混合式」是由於他們不只僅進行正常的新生代垃圾收集,同時也回收部分後臺掃描線程標記的分區。混合式垃圾收集以下圖:
混合式GC也是採用的複製的清理策略,當GC完成後,會從新釋放空間。
堆內存被劃分爲固定大小的多個區域.
每一個heap區(Region)的大小在JVM啓動時就肯定了. JVM 一般生成 2000 個左右的heap區, 根據堆內存的總大小,區的size範圍容許爲 1Mb 到 32Mb.
實際上,這些區域(regions)被映射爲邏輯上的 Eden, Survivor, 和 old generation(老年代)空間.
圖中的顏色標識了每個區域屬於哪一個角色. 存活的對象從一塊區域轉移(複製或移動)到另外一塊區域。設計成 heap 區的目的是爲了並行地進行垃圾回收(的同時中止/或不中止其餘應用程序線程).
如圖所示,heap區能夠分配爲 Eden, Survivor, 或 old generation(老年代)區. 此外,還有第四種類型的對象被稱爲 區域(Humongous regions),這種巨無霸區是設計了用來保存比標準塊(standard region)大50%及以上的對象, 它們存儲在一組連續的區中. 最後一個類型是堆內存中的未使用區(unused areas).
備註: 截止英文原文發表時,巨無霸對象的回收尚未獲得優化. 所以,您應該儘可能避免建立太大(大於32MB?)的對象.
堆被分爲大約2000個區. 最小size爲1 Mb, 最大size爲 32Mb. 藍色的區保存老年代對象,綠色區域保存年輕代對象.
注意G1中各代的heap區不像老一代垃圾收集器同樣要求各部分是連續的.
存活的對象被轉移(copied or moved)到一個/或多個存活區(survivor regions). 若是存活時間達到閥值,這部分對象就會被提高到老年代(promoted to old generation regions).
此時會有一次 stop the world(STW)暫停. 會計算出 Eden大小和 survivor 大小,給下一次年輕代GC使用. 清單統計信息(Accounting)保存了用來輔助計算size. 諸如暫停時間目標之類的東西也會歸入考慮.
這種方法使得調整各代區域的尺寸很容易, 讓其更大或更小一些以知足須要.
存活對象被轉移到存活區(survivor regions) 或 老年代(old generation regions).
剛剛被提高上來的對象用深綠色顯示. Survivor 區用綠色表示.
總結起來,G1的年輕代收集概括以下:
和 CMS 收集器類似, G1 收集器也被設計爲用來對老年代的對象進行低延遲(low pause)的垃圾收集. 下表描述了G1收集器在老年代進行垃圾回收的各個階段.
G1 收集器在老年代堆內存中執行下面的這些階段. 注意有些階段也是年輕代垃圾收集的一部分.
階段 | 說明 |
---|---|
(1) 初始標記(Initial Mark) | (Stop the World Event,全部應用線程暫停) 此時會有一次 stop the world(STW)暫停事件. 在G1中, 這附加在(piggybacked on)一次正常的年輕代GC. 標記可能有引用指向老年代對象的survivor區(根regions). |
(2) 掃描根區域(Root Region Scanning) | 掃描 survivor 區中引用到老年代的引用. 這個階段應用程序的線程會繼續運行. 在年輕代GC可能發生以前此階段必須完成. |
(3) 併發標記(Concurrent Marking) | 在整個堆中查找活着的對象. 此階段應用程序的線程正在運行. 此階段能夠被年輕代GC打斷(interrupted). |
(4) 再次標記(Remark) | (Stop the World Event,全部應用線程暫停) 完成堆內存中存活對象的標記. 使用一個叫作 snapshot-at-the-beginning(SATB, 起始快照)的算法, 該算法比CMS所使用的算法要快速的多. |
(5) 清理(Cleanup) | (Stop the World Event,全部應用線程暫停,併發執行) 在存活對象和徹底空閒的區域上執行統計(accounting). (Stop the world) 擦寫 Remembered Sets. (Stop the world) 重置空heap區並將他們返還給空閒列表(free list). (Concurrent, 併發) |
(*) 拷貝(Copying) | (Stop the World Event,全部應用線程暫停) 產生STW事件來轉移或拷貝存活的對象到新的未使用的heap區(new unused regions). 只在年輕代發生時日誌會記錄爲 `[GC pause (young)]`. 若是在年輕代和老年代一塊兒執行則會被日誌記錄爲 `[GC Pause (mixed)]`. |
順着定義的階段,讓咱們看看G1收集器如何處理老年代(old generation).
存活對象的初始標記被固定在年輕代垃圾收集裏面. 在日誌中被記爲 GC pause (young)(inital-mark)
。
若是找到空的區域(如用紅叉「X」標示的區域), 則會在 Remark 階段當即移除. 固然,"清單(accounting)"信息決定了活躍度(liveness)的計算.
空的區域被移除並回收。如今計算全部區域的活躍度(Region liveness).
G1選擇「活躍度(liveness)」最低的區域, 這些區域能夠最快的完成回收. 而後這些區域和年輕代GC在同時被垃圾收集 . 在日誌被標識爲 [GC pause (mixed)]
. 因此年輕代和老年代都在同一時間被垃圾收集.
所選擇的區域被收集和壓縮到下圖所示的深藍色區域和深綠色區域.
總結下來,G1對老年代的GC有以下幾個關鍵點:
參考資料:
<<深刻理解JVM&G1 GC>>
https://tech.meituan.com/g1.html
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html
https://www.ps.uni-saarland.de/courses/gc-ws01/slides/generational_gc.pdf
http://www.oracle.com/technetwork/cn/articles/java/g1gc-1984535-zhs.html
https://liuzhengyang.github.io/2017/06/07/garbage-first-collector/
http://blog.jobbole.com/109170/
https://github.com/cncounter/translation/blob/master/tiemao_2014/G1/G1.md
https://segmentfault.com/a/1190000007795862
http://moguhu.com/article/detail?articleId=56
https://www.javadoop.com/post/g1
https://blog.csdn.net/zero__007/article/details/52797684