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)

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

<!--[if !supportLists]-->ü <!--[endif]-->優缺點:在空間中存活對象較多的情況下較爲高效,但由於該算法爲直接回收不存活對象所佔用的內存,因此會造成內存碎片。

 

標記-壓縮(Mark-Compact)

<!--[if !supportLists]-->ü <!--[endif]-->算法:標記階段與“標記-清除”算法相同,但在清除階段有所不同。在回收不存活對象所佔用的內存空間後,會將其他所有存活對象都往左端空閒的空間進行移動,並更新引用其對象指針。

<!--[if !supportLists]-->ü <!--[endif]-->過程:

 

<!--[if !supportLists]-->ü <!--[endif]-->優缺點:在“標記-清除”的基礎上還需要進行對象移動,成本相對較高,好處則是不產生內存碎片。

 

 

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默認策略及組合策略

<!--[if !supportLists]-->ü   <!--[endif]-->ClinetServer模式默認GC策略

 

新生代GC方式

舊生代和持久代GC方式

Client

串行GC

串行GC

Server

並行回收GC

Parallel Mark Sweep GC

 

 

<!--[if !supportLists]-->ü   <!--[endif]-->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

 

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