深入理解Java虛擬機學習筆記:第三章,垃圾收集器

第三章,垃圾收集器

概述

一、概述

1.垃圾收集器(GC)需要思考的三件事情:

    哪些內存需要回收?
    什麼時候回收?
    如何回收?

2.Java內存運行數據區域中,程序計數器、虛擬機棧、本地方法棧、三個區域跟線程的聲明週期相同。方法結束或者線程結束時,內存就跟着回收了。而Java堆和方法區不同,我們只有程序處於運行期間纔會知道創建那些對象,這部分內存的分配和回收都是動態的。垃圾收集器所關注的就是這部分。

二、如何判斷對象已經死亡

    垃圾收集器在回收對象之前,第一步就是要判斷對象是否應該回收。

1.引用計數算法:
(1)給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器的值就減1;任何時刻計數器爲0的對象不可能在被使用。
(2)優點:實現簡單,判定效率高。
(3)缺點:很難解決對象之間相互循環引用的問題。
2.可達性分析算法:
(1)通過一系列的“GC Roots”的對象作爲起始點,從這些節點開始向下搜索,所走過的路徑稱之爲引用鏈,當一個對象到GC Roots沒有任何引用時,就是GC Roots到這個對象不可達,證明對象是不可用的。
(2)GC Roots對象包括以下幾種:

    虛擬機棧(棧幀中的本地變量表)中引用的對象。
    方法區中類靜態屬性引用的對象。
    方法區中常量引用的對象。
    本地方法棧中JNI(即一般說的Native方法)引用的對象。

(3)引用:JDK1.2之後,對引用的概念進行了擴充,將引用分爲強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4種。

    強引用就是指在程序代碼之中普遍存在的,只要強引用還在,GC永遠都不會回收掉被引用的對象。
        類似於“Object obj = new Object()”這類的引用。
    軟引用是用來描述一些有用但並非必須的對象。在系統將要發生內存溢出異常時,纔會回收軟引用。
        如何回收完後仍然沒有足夠的內存,則會拋出內存溢出異常。
        SoftReference類來實現軟引用。
    弱引用是用來描述非必須對象的。關聯的對象可以生存到下一次GC發生之前。只要GC工作,就會回收弱引用。
        WeakReference類來實現弱引用。
    虛引用成爲幽靈引用或者幻影引用,最弱的一種引用關係。
        PhantomReference類來實現虛引用。

(4)在可達性分析算法中,不可達對象判斷爲不可達到真正被回收至少要經過兩次標記過程。第一次爲經過分析沒有和GC Roots引用鏈相連,會被第一次標記並且進行一次篩選。判斷對象是否需要執行finalize()方法。

    如果對象沒有覆蓋finalize方法,或者說finalize方法已經調用過,虛擬機將這兩種情況都視爲“沒有必要執行。”
    任何一個對象的finalize方法只會調用一次。

(5)回收方法區。方法區主要回收兩部分內容,包括廢棄常量和無用的類。

    回收廢棄常量和回收Java堆的對象非常相似,如何常量沒有任何引用。則被認爲是廢棄常量。
        其他類(接口)、方法、字段的符號引用也與此類似。
    廢棄的類需要滿足以下三個條件:
        該類的所有實例都已經被回收,Java堆中不存在該類的任何實例。
        加載該類的ClassLoader已經被回收。
        該類對應的java.lang.Class對象沒有在任何地方被引用,在任何地方都無法通過反射訪問該類的方法。

三、垃圾收集算法

1.標記 - 清除 算法
(1)思想:首先標記出所有要清理的對象,在標記完成之後他統一回收所有被標記的對象。
(2)不足:

    效率問題:標記和清除兩個過程的效率都不高。
    空間問題:標記清除算法容易產生不連續的內存碎片。
        PS:內存碎片太多將會導致在分配大對象的時候無法找到連續的內存空間而不得不提前觸發另一次垃圾收集操作。

2.複製算法
(1)解決 標記 - 清除 算法的效率問題。
(2)適用場景:新生代GC,新生代特點,對象存活時間短,存活率低,回收率高。
(3)思想:將內存按照劃分成大小相等的兩塊,每次將活着的對象複製到另一塊內存中。然後將另一半清理掉。
(4)真實內存劃分:根據IBM公司研究表明,98%的對象都是朝生夕死的,所以並不需要1:1分配內存空間。只需要將內存空間分爲一塊較大的空間Eden區以及兩塊較小的空間Survivor區。HotSpot虛擬機默認Eden區和Survivor區的大小比例爲8:1,也就是說每次使用新生代80%的空間。
(5)複製算法時,內存不足:如果在將Eden和一個Survivor區中的對象複製到另一半Survivor區,而另一半Survivor內存不足時,將會通過分配擔保進入其他內存(這裏指老年代)

3.標記 - 整理算法
(1)解決 標記 - 清除 算法的生成內存碎片的問題。
(2)適用場景:老年代GC,老年代特點,對象存活率較高,沒有額外空間。
(3)思想:將內存中的對象標記,然後將存活的對象向一端移動,直接清理掉端邊界以外的內存。

4.現代商業虛擬機大都採用“分代收集算法”,指的是根據對象存活週期不同,把內存劃分爲幾個區域,一般都爲新生代和老年代。根據不同區域的特性來配置響應的收集算法。

四、HotSpot 的算法實現

1.枚舉根節點:可達性分析,這項工作需要在保證一致性的快照中進行。枚舉根節點需要停頓。

2.準確式GC:虛擬機可以知道某個位置記錄了什麼數據。在HotSpot實現中,使用一組稱爲OopMap的數據結構來達到這個目的。

3.安全點:在OopMap的協助寫,HotSpot可以快速且準確地完成GC Roots枚舉,但一個很現實的問題是:OopMap內容變化指令非常多,爲每一條指令都生成一個OopMap將會需要大量的額外空間,這樣GC的空間成本非常高。實際上,OopMap只在特定的位置記錄了這些信息,這些位置稱爲“安全點”,即程序運行到這裏不會導致對象的引用關係變化。

4.如何在GC發生時讓線程都跑到安全點停頓下來。有兩種方法,分別是搶斷式中斷和主動式中斷,搶斷式中斷指的是,GC發生時把所有線程全部中斷,發現沒有到安全點的線程恢復起來跑到安全點。主動式中斷指的是設置一個標誌,讓所有線程去輪詢這個標誌,輪詢標誌和安全點是重合的,判斷在輪詢標誌中的線程就自己中斷掛起。當前HotSpot使用的是主動式中斷。

5.安全區域指的是對象的引用不會發生變化的區域。

五、垃圾收集器

七種垃圾收集器的特性、基本原理和使用場景。

1、Serial收集器:Serial收集器是最基本、發展歷史最悠久的收集器。這是一個單線程收集器。它只會使用一個CPU或者一條收集線程去完成垃圾收集工作,而且在垃圾收集過程中,虛擬機中其他線程必須暫停,等待該線程收集完成。
優點:簡單而高效
缺點:需要停頓
算法:新生代採用複製算法,老年代採用 標記-整理 算法。
使用場景:虛擬機Client模式下默認的新生代收集器。

2、parNew收集器:ParNew是Serial的多線程版本。除了使用多線程收集外,其他方面與Serial沒有任何區別。
優點:簡單而高效
缺點:需要停頓
算法:新生代採用複製算法,老年代採用 標記-整理 算法。
使用場景:虛擬機Server模式下默認的新生代收集器。唯一可以配合CMS收集器工作的垃圾收集器。

3、Parallel Scavenge收集器:多線程處理器,吞吐量優先。吞吐量指的是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間 / (運行用戶代碼的時間 + 垃圾收集時間 ) 。提供 -XX:MaxGCPauseMilis 用來控制最大垃圾收集停頓時間。 提供 -XX:GCTimeRatio 用來控制 GC時間佔總時間的比率。提供 -XX:UseAdaptiveSizePolicy用來進行GC自適應調節策略。
優點:能夠控制垃圾收集時間的吞吐量。
缺點:效率相對較低。
算法:新生代採用複製算法,老年代採用 標記-整理 算法。
使用場景:適合後臺運算而不需要太多交互的任務。

4、Serial Old收集器:是Serial的老年代版本,單線程收集器,使用 標記-整理 算法。可作爲CMS收集器的後備預案,在併發手機發生Concurrent Mode Failure時使用。

5、Parallel Old收集器:Parallel Old是Parallel Scavenge收集器的老年代版本,多線程收集器,使用 標記-整理 算法。吞吐量優先原則。

6、CMS收集器:CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。採用 標記-清除 算法。
整個過程需要4個步驟:

    初始標記:需要“Stop The World”,標記一下GC Roots能直接關聯到的對象,速度很快。
    併發標記:是進行GC Roots Tracing的過程。
    重新標記:需要“Stop The World”,修正併發標記期間,用戶程序繼續運行而導致標記產生變動的那一部分標記。停頓時間稍長,但比初始標記短。
    標記清除:和用戶線程併發的收集垃圾清理工作。

由於整個收集過程中耗時最長的兩個部分,併發標記以及標記清除都可以和用戶線程一起運行,所以CMS收集器可以看作是和用戶線程一起運行的。

優點:併發收集、低停頓。
缺點:
    1、對CPU資源非常敏感。默認回收線程數是(CPU數量 + 3)/ 4 。CPU數量越多,CMS消耗資源相對來說越少。
    2、無法處理浮動垃圾。可能出現“ConCurrent Mode Failure”失敗而導致另一次Full GC的產生。
    3、標記-清除算法會產生空間碎片。

7、G1收集器:面向服務器端應用的垃圾處理器。整體來看是採用 標記 - 整理 算法,而局部上來看是基於 複製 算法來實現的。

G1與其他垃圾收集器相比具備如下特點:
1、併發與並行:能使用多個CPU來縮短停頓的時間,多線程收集。
2、分代收集:G1收集器存在分代概念,但是單獨管理整個堆。
3、空間整合:整體基於標記-整理算法,局部基於複製算法。都不會產生空間碎片。
4、可預測的停頓:在縮短停頓時間的基礎上,能夠建立停頓時間模型。

G1收集器將整個堆劃分成多個相同大小的獨立區域(Region),並維護一個優先列表,用於跟蹤記錄每一個Region的回收價值(根據回收獲得的空間大小以及回收所需的時間確定),每次在允許的時間內,優先回收價值最大的Region(這就是Garbage-First名稱的由來)。

虛擬機爲了防止G1收集器掃描全部Region或者其他收集器掃描新生代以及老年代。G1中Region的對象引用以及其他收集器新生代與老年代之間的對象引用,虛擬機使用Remembered Set(RSet)來避免進行全堆掃描。

G1運行大致可以分爲:
1、初始標記:標記一下GC Roots能直接關聯到的對象。
2、併發標記:從GC Roots開始進行可達性分析。
3、最終標記:修正併發標記期間用戶程序運行導致的標記變動。
4、篩選回收:進行Region的回收價值排序並進行垃圾回收。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章