JVM總結(二):垃圾回收器

這一節咱們來總結一下JVM垃圾收集器方面的東西。 
算法

垃圾回收器

判斷對象引用是否失效

對象生存判斷算法

  • 引用計數法 
    給對象中添加一個引用計數器,每當一個地方引用到這個對象的時候,計數器值就加1,當引用失效時,計數器的值就減1,當計數器值變爲0時,便說明該對象不可能再被使用了。 
    優勢:實現簡單,斷定效率較高。 
    缺點:當出現對象之間的相互循環引用時,即兩個類中都存在引用字段分別引用着對方的時候,在回收過程當中這時該算法無效。
  • 可達性分析算法 
    爲了克服引用計數法的弊端,如今比較主流的實現算法是可達性分析算法。該算法的基本思想是經過一系列的成爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈。若是在一次的搜索中,一個對象到GC Roots沒有任何的引用鏈相連,則說明此對象是不可用的。具體如圖所示 

  GC Roots的斷定: 
  一、虛擬機棧(棧幀中的本地變量表)中引用的對象 
  二、方法區中靜態屬性引用的對象 
  三、方法區中常量引用的對象 
  四、本地方法棧中JNI(即通常說的Native方法)引用的對象
多線程

引用判斷過程

判斷引用是否無效的過程分爲三個階段 
一、當JVM進行垃圾收集時,JVM使用可達性分析算法進行分析,若是對象在進行可達性分析後發現沒有與GC Roots相鏈接的引用鏈,此時該對象將被第一次標記,並進行一次篩選,篩選的條件是此對象有沒有必要執行finalize()方法,若是對象沒有覆蓋該方法,或者該方法已經被虛擬機調用過了,虛擬機將這兩種狀況都視爲「沒有必要執行」。 
二、若是該對象被斷定爲有必要執行finalize()方法,那麼對象將會被放置到一個叫作F-Queue的隊列中,並在稍後由一個由虛擬機自動創建的、低優先級的Finalizer線程去執行它。這裏所謂的執行是指虛擬機會觸發這個方法,但並不承若會等待它運行結束。由於一個對象可能在finalize()方法中執行緩慢,或者發生了死循環,這將致使該隊列中的其餘對象長期處於等待階段,甚至致使整個內存系統的奔潰。 
三、F-Queue中的標記篩選。 
finalize()方法是對象逃脫死亡命運的最後一次機會,而後GC將對F-Queue中的對象進行第二次小規模的標記。若是對象在finalize()方法中成功拯救了本身,即與引用鏈上的任何一個對象創建關聯,那麼在第二次標記的時候,該算法將被移出F-Queue的集合,若是對象這個時候尚未逃脫,那基本上它就真的被回收了。
併發

垃圾收集算法簡介

目前比較主流的垃圾收集算法有四種:標記-清除算法、複製算法、標記-整理算法、分代收集算法。具體分析對好比下:性能

分類 標記-清除算法(Mark-Sweep) 複製算法(Coping) 標誌-整理算法(Mark-Compact) 分代收集算法(Generational Collection)
進行整理 是  
算法實現過程 該算法分爲兩個過程:標記和清除。先標記出全部須要回收的對象,在標記完成後統一回收全部被標記的要回收的對象。 將內存按容量劃分爲大小相等的兩塊區域,每次使用其中的一塊,當一塊的內存用完了,執行GC算法時將還存活的對象整理複製到另一塊上,而後清理全部的內存塊。 該算法分爲兩個過程:標記和整理。首先標記出全部須要回收的對象,而後讓存活的對象都向內存的一端移動,而後直接清除掉端邊界之外的內存。 根據對象存活週期的不一樣將內存劃分爲幾塊,通常是劃分爲新生代和老年代,而後根據各個年代的特色採用不一樣的最適當的收集算法。
優勢 簡單,易於實現 內存分配時算法不產生內存碎片 內存分配時算法不產生內存碎片,也比較易於實現 分代收集,效率較高
缺點 一、效率低 二、會產生大量不連續的內存碎片 空間消耗太大,內存被壓縮爲原來的一半 算法複雜度大,執行步驟較多 算法複雜度大,執行步驟較多

垃圾收集器

常見的JVM垃圾收集器有七種,具體以下圖所示: 

spa

新生代垃圾收集器

  • Serial 
    Serial收集器是單線程的一個收集器,但它的單線程的意義是它只會使用一個CPU或者一條收集線程去完成垃圾收集工做,更重要的是在它進行垃圾收集的時候,必須暫停其餘全部的工做線程,直到它收集結束。 
    分代收集算法:新生代單線程採用複製算法,並暫停全部用戶線程;老年代單線程採用標記-整理算法,並暫停全部用戶線程。
  • ParNew 
    ParNew收集器是Serial收集器的多線程版。其基本操做和Serial算法基本一致。該收集器通常搭配CMS收集器進行工做。‘ 
    分代收集算法:新生代採用複製算法,並暫停全部用戶線程;老年代採用標記-整理算法,並暫停全部用戶線程。
  • Parallel Scavenge 
    Parallel Scavenge收集器是也與ParNew算法十分類似,可是與其餘收集器的關注點大可能是儘量縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器目的是達到一個可控制的吞吐量。吞吐量就是CPU用於運行用戶代碼的時間與CPU總消耗的時間的比值。即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),舉個例子,虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。 
    GC自適應調節策略:JVM會根據當前系統的運行狀況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者最大吞吐量。Parallel Scavenge收集器能夠搭配自適應調節策略。 
    分代收集算法:新生代採用複製算法,並暫停全部用戶線程;老年代採用標記-整理算法,並暫停全部用戶線程。

老年代垃圾收集器

  • Serial Old 
    Serial Old是Serial算法的老年代版本,一樣是一個單線程收集器。該收集器主要是給Client模式下的虛擬機使用的。 
    分代收集算法:新生代單線程採用複製算法,並暫停全部用戶線程;老年代單線程採用標記-整理算法,並暫停全部用戶線程。
  • Parallnel Old 
    Parallnel Old是Parallel Scavenge收集器的老年代版本,使用多線程和「標記-整理」算法。
  • CMS 
    CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。該收集器是基於」標記-清除「算法實現的。 
    CMS收集器的收集過程分爲如下4個步驟: 
    一、初始標記(Stop the World,標記GC Roots能直接關聯到的對象) 
    二、併發標記(進行GC Roots Tracing的過程) 
    三、從新標記(Stop the World,休整併發標記期間因用戶程序繼續運行而致使標記產生變更的那一部分對象的標記記錄) 
    四、併發清除(併發清除無用的對象) 
    缺點: 
    a、CMS收集器對CPU資源很是敏感,併發階段佔用的線程資源較多。 
    b、CMS收集器沒法處理浮動垃圾。由於CMS併發清理階段用戶線程還在運行着,因此也會有相應的垃圾產生,這部分垃圾CMS沒法在這次的收集中處理掉它們。 
    c、CMS收集器因爲是基於「標記-清除」算法,故會產生較多的內存空間碎片。

新生代和老年代垃圾收集器

  • G1 
    G1(Garbage-First)收集器所具有的特色: 
    一、並行和併發:使用多個CPU來縮短Stop-The-World的時間,部分垃圾收集器本來須要停頓Java線程執行的GC動做,G1收集器仍然能夠經過併發的方式讓Java程序繼續執行。 
    二、分代收集 
    三、空間整合:標記-整理算法。 
    四、可預測的停頓。追求低停頓,並創建可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M毫秒的時間片斷內,消耗在垃圾收集上的時間不得超過N毫秒,達到了實時Java的垃圾收集器。 
    G1收集器分代策略: 
      G1收集器將整個Java堆劃分爲多個大小相等的獨立區域(Region)。G1收集器之因此能夠有計劃地避免在整個Java堆中進行全區域的垃圾收據,是由於G1收集器跟蹤各個Region裏面的垃圾堆積的價值大小(回收得到的空間大小以及回收所需時間的經驗值),在後臺維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的Region。即Grabage-First。 
         在G1收集器中,Region之間的對象引用以及其餘收集器中的新生代與老年代之間的對象引用,虛擬機都是經過 
    Remembered Set來避免全堆掃描的。G1中每一個Region都有一個與之對應的Remembered Set。在新建對象時,JVM會將相關的引用信息記錄到被引用對象所屬的Region的Remembered Set中。當進行回收時,在GC根節點的枚舉範圍中加入Remembered Set便可保證不對堆進行掃描也不會有遺漏。 
    G1收集器的手機階段也分如下幾個步驟: 
    一、初始標記(只是標記一下GC Roots能直接關聯到的對象,並修改能夠得Region中建立新對象,這階段須要停頓線程,但耗時很短) 
    二、併發標記(從GC Roots開始對堆中對象進行可達性分析,找出存活對象) 
    三、最終標記(修正在併發標記期間因月洪湖程序繼續運行而致使標記產生變更的那一部分標記記錄) 
    四、篩選回收(首先對各個Regin的回收價值和成本進行排序,根據用戶所期待的GC停頓時間指定回收計劃,回收一部分Region)

最後,咱們總結一下JVM中的垃圾收集器:線程

 

分類 所屬分代 使用線程 使用算法
Serial 新生代 單線程 複製(新)、標記-整理(老)
ParNew 新生代 多線程 複製(新)、標記-整理(老)
Parallel Scavenge 新生代 多線程 吞吐量優先算法
Serial Old 老生代 單線程 複製(新)、標記-整理(老)
Parallel Old 老生代 多線程 複製(新)、標記-整理(老)
CMS 老生代 多線程 標記-清除算法(初始標記、併發標記、從新標記、併發清除)
G1 新生代&&老生代 多線程 標記-整理算法(初始標記、併發標記、最終標記、篩選回收)
相關文章
相關標籤/搜索