JVM虛擬機學習(3)---JVM垃圾回收

目錄頁:https://mp.csdn.net/postedit/95937156

1. 小聲嗶嗶

    作爲一個Java程序員,我們不需要像C程序員那樣對內存釋放有過多的關心,但是我覺得還是有必要了解一下JVM的垃圾回收機制。我們知道,GC主要有兩種,分別是新生代滿的時候觸發的Minor GC和老年代滿的時候觸發的Full GC。

2. GC算法

2.1. 標記-清除算法

    見名知意,標記清除算法就是將算法分爲標記和清除兩個階段,將需要回收的對象標記後進行回收。該算法是比較基礎的算法,但是存在兩個缺點:

  1. 效率不高,標記和回收兩個步驟的效率都不高。
  2. 空間問題:標誌清除後會產生大量的不連續內存碎片,後續需要分配大對象時會無法找到足夠內存空間而觸發垃圾回收。

這裏說該算法是基礎算法的原因是後續的算法都是基於該思路並針對其不足點改進。

2.2. 複製算法

    複製算法的玩法就是將內存分爲大小相等的兩塊,每次只使用一塊,需要垃圾回收時,先將存活對象複製到另一塊上面,再將已使用的內存空間全部回收。這樣每次回收都是對整個半區進行操作,不用考慮內存碎片的情況,只需要移動堆指針按照順序分配集合。但是代價就是將內存縮小到了原來的一半。

    實際使用中,新生代中的對象98%都是會很快被釋放的,並不需要1:1的來進行內存分配,而是將內存分爲一塊較大的Eden空間和兩塊較小的Survivor區,每次使用Eden和其中一塊Survivor,當回收時將Eden和Survivor中還存活的對象一次性的複製到另一塊survivor空間上,最後清理掉Eden和Survivor空間。我們常用的HotSpot虛擬機默認使用的就是這種算法,新生代分爲了Eden和兩個Survivor區,比例爲8:1:1,也就是新生代可用內存爲整個新生代的90%。這時問題來了,Survivor空間不足以支撐存活對象複製時就需要依賴老年代進行分配擔保。這裏的分配擔保是指當Survivior區不足時,會有一部分對象被直接複製至老年代。

2.3. 標記整理算法

    標記整理算法的標記過程與標記清除算法一樣,只是後續步驟不是直接對可回收對象進行清理,而是讓所有對象往一端移動,然後直接處理掉邊界外的內存即可。

2.4. 分代收集算法

    這個算法更像是一種對內存的劃分邏輯,根據對象的存活週期將內存劃分爲幾塊,一般是分爲新生代和老年代,這樣就可以根據各個年代的特點進行垃圾回收。在新生代,每次垃圾回收時都會發現有大量的對象死去,只有少量存活,就可以選擇複製算法。老年代中對象存活率高,且沒有多餘的內存來給他做分配擔保,就需要採用標記清理或標記整理算法。

3. JVM中的垃圾回收器

    垃圾回收器就依賴於各JVM虛擬機的具體實現了,這裏只需要瞭解到垃圾回收器分爲單線程,多線程,並行和併發的類型即可,都是開發,單線程多線程就不多說,並行是指垃圾收集的多線程的同時進行。併發:垃圾收集的多線程和應用的多線程同時進行,主要的垃圾回收器如下圖所示:

新生代垃圾回收器:

收集器

收集對象和算法

收集器類型

Serial

新生代,複製算法

單線程

ParNew

新生代,複製算法

並行的多線程收集器

Parallel Scavenge

新生代,複製算法

並行的多線程收集器

 

老年代垃圾回收器:

收集器

收集對象和算法

收集器類型

Serial Old

老年代,標記整理算法

單線程

Parallel Old

老年代,標記整理算法

並行的多線程收集器

CMS

老年代,標記清除算法

並行與併發收集器

G1

跨新生代和老年代;標記整理 + 化整爲零

並行與併發收集器

3.1. CMS垃圾回收器

    CMS垃圾回收器基於標記清除算法,分爲4個步驟:

  1. 初始標記—暫停
  2. 併發標記—同時進行
  3. 重新標記—暫停
  4. 併發清除—同時進行

    初始標記是對GC Root能關聯到的對象進行標記,速度很快,重新標記階段是爲了修正併發標記期限因用戶程序運作導致標記變動的那一部分對象進行標記,這個階段的停頓會比初始標記階段長些,但是比並發標記時間短。

    Concurrent Mark Sweep收集器運行示意圖

    由於整個過程暫停時間較短,且併發標記與併發清除可以與用戶線程一起工作,所以系統停頓時間短,符合現在大部分網站或B/S系統服務要求,但是其仍存在以下三個問題:

  1. 對CPU敏感:併發階段,CMS默認啓動的線程數是(CPU數量+3)/4,也就是當CPU資源在4個以上時,併發回收時垃圾收集線程會佔用不少於25%的CPU資源,但是當CPU資源不足4個時CMS對用戶程序的影響就會變的很大,比如2個CPU時會佔用50%的CPU資源。
  2. 會產生浮動垃圾,可能出現“Concurrent Mode Failer”失敗導致另一次Full GC:由於併發清除階段用戶線程仍在運行,在此階段有可能會有對象被釋放且未被標記,只能下次GC時再進行回收。併發清除階段還需要預留一部分空間給用戶線程使用。
  3. 內存碎片:由於CMS採用的是標記清除算法,會產生內存碎片,導致大對象分配會比較麻煩,爲解決這個問題,CMS提供了參數-XX:+UseCMSComactAtFullCollection參數(默認開啓),用於在FullGC時開啓內存整理,內存整理過程無法與程序併發執行,空間碎片的問題解決了但是會導致停頓時間變長。

可以使用jps -v命令查看JVM啓動參數

3.2. G1垃圾回收器

    G1(Garbage-First)收集器是當今收集器技術發展的最前沿結果,JDK1.9以後Oracle公司建議使用該垃圾回收器進行垃圾回 收。

    G1收集器將整個JAVA堆劃分爲多個大小相等的獨立區域,雖然還保留有新生代和老年代的概念,但是已經不再是物理隔離的了,而是一部分獨立區域的集合,甚至這部分區域不需要連續。

    除了“化整爲零”這一思路,G1還具備以下特點:

  1. 並行與併發: G1利用多CPU,多核環境下的硬件優勢,使用多個CPU來縮短stop-the-world的停頓時間。
  2. 分代收集:分代概念在G1中仍得以保留。
  3. 空間整合:G1整體是基於標記整理算法實現的收集器,從局部(兩個獨立區域)來看是基於複製算法實現,這就意味着不會出現內存碎片,也就不會由於需要分配大對象時獲取不到連續的內存空間而提前觸發下一次GC
  4. 可預測的停頓:G1可以有計劃的避免在整個Java堆中進行全區域的垃圾收集。G1跟蹤各個區域中的垃圾堆積的價值大小,在後臺維護一個優先列表,每次根據允許的收集時間,優先回收價值最大的區域,既保證的收集效率,又能控制停頓時間。

    G1收集器中,每個區域(Region)都有一個與之對應的Remember Set來避免全堆掃描。G1中每個Region都有一個與之對應的Remember Set,虛擬機發現程序在對Reference類型的數據進行寫操作時,會產生一個Write Barrier暫時中斷寫操作,檢查Reference引用的對象是否處於不同的Region之中,如果是就通過CardTable把相關引用信息記錄到被應用對象所屬的Region的Remember Set中。當進行垃圾回收時,在GC根節點的枚舉範圍中加入Remember Set即可保證不對全堆掃描也不會有遺漏。

    G1的運作可分爲以下幾個部分:

1, 初始標記,2.併發標記,3.最終標記,4.篩選回收

4. 思維導圖

參考資料:周志明大神-《深入理解Java虛擬機 JVM高級特性與最佳實踐》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章