1. 程式人生 > >Java虛擬機器的JVM垃圾回收機制

Java虛擬機器的JVM垃圾回收機制

1.JVM記憶體空間

  JVM堆(Heap)= 新生代(Young) + 舊生代(Tenured)

 分割槽作用: 

新建立的物件通常先將其分配在新生代中,在新生代中經過若干次GC之後仍未釋放的物件,再將它移動到舊生代。為了讓記憶體回收更高效(GC會暫停JVM中的應用),Sun JDK在1.2開始對堆採用了分代管理的方式。在分配物件遇到記憶體不足時,先對新生代進行GC(Young GC);當新生代GC之後仍無法滿足記憶體空間分配需求時, 才會對整個堆空間以及方法區進行GC (Full GC)

 相關引數:

-Xms                       -- 設定堆記憶體初始大小

 -Xmx                       -- 設定堆記憶體最大值

-XX:MaxTenuringThreshold  -- 設定物件在新生代中存活的次數

-XX:PretenureSizeThreshold  -- 設定超過指定大小的大物件直接分配在舊生代中

 注意點:當新生代設定得太小時,也可能引發大物件直接分配到舊生代中。

  新生代(Young)= Eden區 + Survivor區

 分割槽作用:

Eden區為物件通常最初分配到的地方,Survivor區分為S0和S1兩塊大小相等的區域。JVM進行Minor GC時,將Eden中還存活的物件拷貝到Survivor區中,還會將Survivor區中還存活的物件拷貝到Tenured區中。在這種GC模式下,JVM為了提升GC效率, 將Survivor區分為S0和S1,這樣就可以將物件回收和物件晉升分離開來。

 相關引數:

-Xmn             -- 設定新生代記憶體大小。

-XX:SurvivorRatio  -- 設定Eden與Survivor空間的大小比例

 注意點: 圖中Virtual部分表示可伸縮的記憶體空間,當用-Xms在指定堆的初始大小為128m,通過-Xmx指定堆最大為256m時,JVM會根據記憶體情況在128m與256m之間伸縮。為了避免JVM進行這些伸縮消耗效能,對於能夠提供穩定記憶體空間的用作伺服器的JVM,通常將-Xms和-Xmx設定為相等。

  方法區(Perm)

 分割槽作用:

也被成為持久代,用來存放JVM載入的型別資訊。包括: 型別基本資訊,常量池,欄位資訊,方法資訊,類變數,指向ClassLoader的引用,Class類的引用,方法表等。方法區是全域性共享的,在一定條件下也會被GC。

 相關引數:

-XX:PermSize        --設定Perm區的初始大小

-XX:MaxPermSize    --設定Perm區的最大值

  JVM方法棧(注意:不是分割槽)

 作用: 

JVM方法棧為JVM執行緒私有記憶體,當方法執行完畢後,其對應的棧幀記憶體會自動釋放

 相關引數:

-Xss                  --設定方法棧的最大值

 TLAB:

JVM所佔用的主要記憶體都是從堆空間分配的,堆是所有執行緒共享的,因此在堆上分配記憶體需要加鎖,Sun JDK為提升效率,會為每個新建的執行緒在Eden上分配一塊獨立的空間由該執行緒獨享,這塊空間稱為TLAB(Thread Local Allocation Buffer)。其大小由JVM根據執行情況計算得到,也可通過引數-XX:TLABWasteTargetPercent來設定TLAB可佔用的Eden空間的百分比,預設值為1%。在TLAB上分配記憶體不需要加鎖,因此JVM在給執行緒中的物件分配記憶體時會盡量在TLAB上分配。如果物件過大或TLAB用完,則仍然在堆上進行分配。

2.Sun JDK GC收集器

收集器主要分為引用計數器和跟蹤收集器兩種,Sun JDK中採用跟蹤收集器作為GC實現策略。

  跟蹤收集器簡介

跟蹤收集器採用集中式的管理方式,全域性記錄資料的引用狀態。觸發執行時需要從根節點來掃描物件的引用關係,可能會造成應用程式暫停。主要有三種實現演算法:複製(Copying) 、 標記-清除(Mark-Sweep) 、 標記-壓縮(Mark-Compact) 。下文將簡單介紹這三種演算法的過程,有助於後續的GC策略理解和分析。

  複製(Copying)

 演算法:複製採用的方式為從根集合掃描出存活的物件,並將找到的存活物件複製到一塊新的完全未使用的空間中。  過程:注意,紅叉為不存活的物件所佔用記憶體空間

  標記-清除(Mark-Sweep)

 演算法:標記-清除採用的方式為從根集合開始掃描,對存活的物件進行標記,標記完畢後,再掃描整個空間中未標記的物件,並進行回收。  過程:

ü   優缺點:在空間中存活物件較多的情況下較為高效,但由於該演算法為直接回收不存活物件所佔用的記憶體,因此會造成記憶體碎片。

標記-壓縮(Mark-Compact)

ü   演算法:標記階段與“標記-清除”演算法相同,但在清除階段有所不同。在回收不存活物件所佔用的記憶體空間後,會將其他所有存活物件都往左端空閒的空間進行移動,並更新引用其物件指標。

ü   過程:

ü   優缺點:在“標記-清除”的基礎上還需要進行物件移動,成本相對較高,好處則是不產生記憶體碎片。

3.  Sun JDK GC策略

圖4 Sun JDK中可用的GC方式

基於上一小節講解的跟蹤收集器演算法,Sun JDK在新生代和老生代進行了不同的演算法實現,形成了上圖中的GC方式分佈。本小節將具體介紹新生代和老生帶的GC策略及組合方式。

  新生代 – 序列GC(Serial Copying)

 演算法:複製(Copy)

 過程:

 1. 掃描出新生代中存活的物件;

 2. Minor GC將存活的物件複製到做為To Space的S0/S1區;

 3. 之前做為To Space/From Spache的S0/S1區對換角色;

 4. 經歷過幾次Minor GC仍然存活的物件,放入老生代。

  新生代 – 並行回收GC(Parallel Scavenge)

 演算法:複製(Copy)

 過程:在掃描和複製時均採用多執行緒方式進行(如下圖),並且並行回收GC為大的新生代回收做了很多優化(可以自行擴充套件閱讀相關資料)。

 優勢:在多CPU的機器上其GC耗時會比序列方式短,適合多CPU、對暫停時間要求較短的應用。  配置方式:本身是Server級別多CPU機器上的預設GC方式,也可以通過-XX:+UseParallelGC來指定,並且可以採用-XX:ParallelGCThread來指定執行緒數。

  新生代 – 並行GC(ParNew)

 演算法:複製(Copy)  過程:與並行回收GC(Parallel Scavenge)的區別在於並行GC(ParNew)必須配合老生代使用CMS GC。原因是CMS GC在進行老生代GC時,有些過程是併發執行的。如果此時發生了Minor GC,需要進行相應處理,而並行回收GC(Parallel Scavenge)是沒有做這些處理的。也正是如此,ParNew GC不可與並行的老生代GC同時使用。  配置方式:在配置為CMS GC的情況下,新生代預設使用並行GC(ParNew)方式,也可以通過-XX:+UseParNewGC來指定。

  老生代 – 序列GC(Serial MSC)

 演算法:Mark-Sweep-Compact,該演算法結合Mark-Sweep和Mark-Compact做了一些改進。  過程:  1. 掃描出老生代中存活的物件,進行標識;  2. 遍歷整個老生代和持久代記憶體空間,找出未被標識的物件,並回收其記憶體;  3. 進行滑動壓縮(Sliding Compaction),將存活物件向老生代空間的開始處滑動,最終留出一塊連續的到結尾的記憶體空間。  優缺點:序列執行的過程中為單執行緒,需要暫停應用並耗時較長。  配置方式:是client模式預設採用的GC方式,也可以通過-XX:UseSerialGC進行指定。

  老生代 – 並行GC(Parallel Mark Sweep、Parallel Compacting)

 演算法:Mark -Compact  過程:  1. 將老生代劃分為並行執行緒個數的區域(regions);  2. 並行進行存活物件掃描和標記;  3. 單執行緒對各區域進行掃描,標記需要壓縮移動的區域;  4. 並行進行物件移動和區域不存活物件的回收。  優缺點:多執行緒同時操作以及dense prefix優化,會縮短應用暫停時間。但由於老生代較大,在掃描和標識物件上需要花費較長時間。  配置方式:通過-XX:+UseParallelGC來指定使用Parallel Mark Sweep;通過-XX:UseParallelOldGC來指定使用Parallel Compacting。

  老生代 – 併發GC(CMS:Concurrent Mark-Sweep GC)

 演算法:Mark –Sweep  過程:  1. 第一次標記(Initial Marking):暫停整個應用,掃描從根集合點到老生代中可直接訪問到的物件,並進行標記;  2. 併發標記(Concurrent Marking):恢復所有應用的執行緒,同時開始併發對之前標記過的物件進行輪循,以標記這些物件可訪問的物件;  3. 重新標記(Remark):暫停整個應用,掃描在第二步中被改變引用關係或新建立的物件,並進行標記;  4. 併發收集(Concurrent Sweeping):恢復所有應用的執行緒,將沒有標記的物件進行單執行緒回收。針對記憶體碎片,CMS會盡量將相鄰的塊重新組裝成一個塊。

 優缺點:如上圖,優點是隻有在第一次標記和重新標記階段需要暫停整個應用,所以能夠做到影響應用響應時間很短。缺點是併發標記和併發收集階段CMS會與應用執行緒爭用CPU資源(用增量CMS模式可以緩解),並且容易產生記憶體碎片,free-list機制會導致Minor GC效率下降。

 配置方法:通過-XX:UseConcMarkSweepGC來啟動老生代CMS GC;通過-XX:+UseCMSCompactAtFullCollection來啟動記憶體碎片整理功能(整理也會暫停應用)。

  4.Sun JDK GC預設策略及組合策略

ü   ClinetServer模式預設GC策略

新生代GC方式

舊生代和持久代GC方式

Client

序列GC

序列GC

Server

並行回收GC

Parallel Mark Sweep GC

ü   Sun JDK GC組合方式

新生代GC

舊生代和持久代GC

-XX:+UseSerialGC

序列GC

序列GC

-XX:+UseParallelGC

並行回收GC

Parallel Mark Sweep GC

-XX:+UseConcMarkSweepGC

並行GC

併發GC

當出現Concurrent Mode Failure時採用序列GC

-XX:+UseParNewGC

並行GC

序列GC

-XX:+UseParallelOldGC

並行回收GC

Parallel Mark Conpact

-XX:+UseConcMarkSweepGC

-XX:-UseParNewGC

序列GC

併發GC

當出現Concurrent Mode FailurePromotion Failed時採用序列GC

不支援的組合方式

1-XX:+UseParNewGC -XX:+UseParallelOldGC

2-XX:+UseParNewGC -XX:+UseSerialGC