垃圾收集器與內存分配策略之篇二:垃圾收集器

5、垃圾收集器java

   若是說收集算法是內存回收的方法論,那麼垃圾收集器就是內存回收的具體實現。因爲java虛擬機規範對垃圾收集器實現沒有任何的規範所以不一樣的廠商,不一樣的版本的虛擬機所提供的垃圾收集器都有可能會有很大的區別,而且通常都會提供參數供用戶根據本身的應用特色和要求組合出各個年代所使用的收集器。算法

虛擬機中所包含的垃圾收集器以下圖:多線程

 

 

 

連線代表明他們能夠組合使用。下面分別對以上垃圾收集器進行說明:併發

01)Serial 是歷史悠久的收集器,在垃圾回收期間或中斷用戶線程,是一個單線程的收集器,在進行垃圾收集的時候暫停其餘全部的工做線程,直至他收集結束。因爲它是在用戶不可見的時候暫停線程,這對許多用戶來講都是不可接受的。適合於單個CPU,單線程的狀況下面,若是在桌面運行程序下面 即Client模式下面虛擬機來講是一個很好的選擇,由於停頓時間很小。是新生代收集器佈局

Serial 收集器的運行過程網站

02)ParNew 收集器線程

  Parnew收集器實際上是serial收集器的多線程版本,除了可使用多線程進行垃圾收集之外,其他行爲包括Serial收集器的可用的全部控制參數。Parnew收集器的運行過程示例圖:設計

 

須要注意的是除了Serial收集器之外,只有ParNew收集器才能與CMS收集器一塊兒工做。ParNew在單核CPU的狀況下面絕對不會有比Serial收集器更好的效果,甚至因爲存在線程的開銷,該收集器在經過超線程技術實現的兩個CPU的環境匯都不能百分百的超越serial收集器。固然隨着CPU數量的增長,他對於GC時系統資源的有效利用仍是頗有好處的。對象

兩種概念的解釋:blog

併發:指用戶線程與垃圾收集線程同時執行(不必定是並行的,多是交替執行),用戶線程在繼續運行,而垃圾收集程序運行於另外一個CPU上面。

並行:指多條垃圾收集線程並行執行,但此時用戶線程處於等待狀態。

03)  Parallel Scavenge收集器

Paraller Scavenge收集器是一個新生代收集器,他也是採用複製算法的收集器,又是並行的多線程收集器。他與ParNew收集器最大的不一樣是Parallel Scavenge收集器要達到一個可控的吞吐量。吞吐量= 運行用戶代碼的時間/(運行用戶代碼時間+垃圾收集器時間)。如:虛擬機共運行了100分鐘,垃圾收集用了1分鐘,那麼吞吐量是99%。停頓時間越短,用戶的體驗就會更好。高的吞吐量能夠高效率的利用CPU的時間,儘快的完成程序的運算任務,主要適合在後臺不須要交互的任務。

 Parallel Scavenge 收集器提供了兩個參數用於精確的控制吞吐量,分別是最大垃圾收集停頓時間的-XX:MaxGcPauseMills 參數以及直接設置吞吐量大小的-XX:GCTimeRatio參數。MaxGcPauseMills參數是一個大於0的毫秒數,收集器將盡可能地保證內存回收花費的時間不超過設定值。若是把停頓時間調小,會致使GC次數頻繁,吞吐量會降低。

GCTimeRatio參數的值應當是一個大於0且小於100的整數,也就是垃圾收集時間佔總數的時間的比率,至關因而吞吐量的倒數。所以Paraller Scavenge收集器也別成爲吞吐量優先的收集器。Paraller Scavenge收集器還有一個參數-XX:+UseAdapterSizePolicy值得關注,這是一個自適應策略的參數,一旦打開了之後,就不須要盡進行手動的指定新生代大小,Eden和Survivor區域的比例等細節參數了。虛擬機會根據當前的系統信息動態的調整這些參數,成爲GC自適應的調節策略。

04)Serial Old收集器

Serial Old收集器是老年代版本,他一樣是一個單線程收集器,這個收集器的主要意義是主要是Client模式下面的虛擬機使用。Serial Old收集器使用的是標記整理算法。用途是:在JDK1.5的版本以前與Paraller Scavenge 收集器搭配使用,另外一種用途是CMS收集器的備選方案。

05) Parallel Old 收集器是Paraller Scavenge 收集器的老年大版本,使用多線程和標記整理算法。由於新生代收集器Parallel Scavenge 收集器沒法與CMS收集器一塊兒工做,因此若是Parallel Scavenge 收集器選擇了在新生代使用,那麼老年代只能使用Parallel  Scavenge收集器。Parallel Old 收集器只能和Parallel Scavenge收集器一塊兒工做搭配。Serial和 ParNew收集器沒法與Parallel Old收集器一塊兒工做。

06)CMS 收集器

CMS 收集器是一個以獲取最短回收停頓時間爲目標的收集器。目前很大一部分的java應用集中在互聯網網站或者B/S系統的服務端上,這類應用尤其重視服務的響應速度,但願停頓時間最短,已給用戶最好的體驗。CMS收集器就很是符合這類應用的需求。

CMS收集器是基於標記清除算法實現的,它的運做過程相對於前面的幾種的收集器來講更復雜一些。整個過程分爲初始標記、併發標記、從新標記、併發清除。其中初始標記和從新標記這兩個步驟任然須要「stop the  world「。初始標記僅僅只是標記一下GC Roots 能直接關聯到的對象,速度很快.併發標記階段就是進行GC Root Tracing的過程,而重現標記階段則是爲了修正併發標記期間因用戶程序運做而致使的那一部分對象的記錄,這個階段的停頓時間通常會比初始標記的時間稍長一些,但遠比並發標記時間短。因爲整個過程當中耗時最長的併發標記和併發清除收集線程都是能夠與用戶線程一塊兒工做,因此,從整體來講,CMS收集器的內存回收過程是與用戶線程一塊兒併發執行的。經過下圖能夠清楚的看出CMS收集器的運行過程和須要停頓的時間:

CMS是一款優秀的收集器,它的主要優勢在名字上面已經體現出來了:併發收集、低停頓,sun公司的一些官方文檔中也稱之爲併發低停頓收集器。可是CMS收集器還遠達不到完美的程度,他有如下三個明顯的缺點:

 1.CMS收集器對CPU資源特別敏感。其實,面向併發設計的程序都對CPU資源比較敏感。在併發階段,他雖然不會致使用戶線程的停頓,可是會爲了佔用了一部分線程而致使應用程序變慢,總吞吐量會下降。CMS默認啓動的回收線程數量是(CPU數量+3)/4 ,也就是當CPU在4個以上的時候,併發回收垃圾收集線程很多於25%的CPU資源,並隨着CPU的數量的增長而降低。可是當CPU不足4個的時候,好比說兩個的時候CMS收集線程對用戶的影響就會很大,若是原本負載就比較大的時候,還分出一部分去執行垃圾收集線程,就可能致使用戶線程執行變慢,這讓人很難接受。爲了解決這一個問題,虛擬機提供了一種增量式併發收集器,思想是:在併發標記、清理的時候讓GC線程、用戶線程交替執行,儘可能減小GC線程獨佔資源的時間,這樣整個垃圾收集的過程會變得更長,可是對用戶程序的影響就會小一些,也就是速度降低沒有那麼明顯。

2.CMS收集器沒法處理浮動垃圾。因爲CMS併發清理階段是在和用戶線程一塊兒執行的,伴隨着程序的運行天然就還會新的垃圾產生,這一部分垃圾在出如今標記過程以後的話,CMS沒法在當次收集過程當中進行處理,只好在下一次的垃圾清理的時候在進行清理,這一部分垃圾成爲浮動垃圾。

3.CMS垃圾收集器是基於的標記清除算法,收集結束後會有大量的空間碎片產生。空間碎片過多的時候,將會給大對象分配帶來很大的麻煩,每每會出現老年代還有不少的剩餘,可是沒法找到足夠大的連續的空間來分配當前的對象,不得不提早觸發一次Full  GC 。爲了解決這個問題,CMS收集器提供了一個 -XX:+UseCMSCompactAtFullCollection開關參數(默認就是開啓的),用於在CMS收集器頂不住的時候,進行FullGC時候進行內存碎片的合併過程,內存整理過程是沒法併發的,空間碎片問題沒有了,可是停頓時間會變得很長。爲此虛擬機提供了另外一個參數 -XX:CMSFullGCsBeforeCompaction,這個參數是用於設置執行多少次不壓縮FullGC,來執行一次帶壓縮的(默認爲0,表示每次full Gc 都進行內存整理)

07) G1收集器

G1是一款面向服務端應用的垃圾收集器。HotSpot開發團隊賦予他的使命是(在比較長的時間)將來可替換jdk1.5的CMS收集器。與其餘收集器相比,G1收集器的特色:

並行與併發:G1能充分利用多CPU、多核環境下的硬件優點,使用多個CPU來縮短stop-the-world停頓的時間,部分其餘收集器本來須要停頓java線程執行的GC動做,G1收集器仍然能夠用併發的方式讓java線程繼續運行。

分代收集:與其餘收集器同樣,分代概念在G1收集器中得已保留。能夠採用不一樣的處理方式去處理新建立的對象和已經存活了一段時間的對象。

空間整合:G1收集器是基於標記整理算法實現的垃圾收集器,不會產生空間碎片。

可預測性停頓:這是G1相對於CMS的另外一個優點。G1除了追求低停頓之外,還能創建可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M毫秒的時間片斷內,消耗在垃圾收集收集上的時間不超過N毫秒,這個幾乎是已是實時java(RTSJ)的垃圾收集器的特徵了。

 

在G1以前的其餘收集都是在老年代和新生代之間進行來及回收的,而G1不在是這樣。使用G1收集器的話,java的堆內存佈局就與其餘的收集器有很大的差異,通它將整個java堆劃分爲多個大小相等的獨立區域,雖然還保留着老年代和新生代的概念,可是新生代和老年代不在是物理隔離了,他們都是一部分Region集合。

G1收集器之因此能創建起來可預測的停頓時間模型,是由於它能夠有計劃的避免在整個java堆中進行全區域的垃圾回收。G1跟蹤各個region裏面的垃圾堆價值大小,在後臺維護一個優先列表,每次都根據容許的收集時間,優先回收價值最大的Region。這種使用Region劃份內存空間以及有優先級的區域回收方式,保證了G1收集器在有限的時間內能夠獲取儘量高的收集效率。

   在G1收集器中,Region之間的對象引用以及其餘收集器的新生代與老年代之間的對象引用,虛擬機都是採用Rememberd Set 來避免全局掃描的。G1中每個Region都有一個與之對應的Remember Set,虛擬機發現程序在對Reference 類型的數據進行寫操做的時候,會產生一個Write Barrier 暫時中斷寫操做,檢查Reference 引用的對象是否處於不一樣的Region中(在分代收集的時候,是檢查是否老年代中的對象引用了新生代的對象),若是是,便經過CardTable 把相關的引用信息記錄到被引用對象的所屬的Region的Remember Set之中。當進行垃圾回收的時候,把GC根節點的枚舉範圍中加入Rememeber Set便可保證不對全局掃描也不會有遺漏。

初始標記階段僅僅只是標記一下GC ROOTS能關聯的對象,而且能修改TAMS的值,讓下一階段用戶程序併發運行時,能在正確可用的Region中建立新的對象,這階段須要停頓線程,但耗時很短。併發標記階段是從GC ROOTS開始對堆中進行可達性分析研究,找出存活的對象,這階段耗時較長,可是能夠與用戶線程併發執行。而最終標記階段是爲了修正正在併發標記標記期間由於用戶線程繼續運做而致使標記產生變更的那一部分標記記錄,虛擬機將這段變更記錄在線程Remember Set Logs 裏面,最終標記階段須要把Remember Set Logs 的數據合併到Remember Set中,這個階段須要停頓線程,可是能夠並行執行。最後篩選回收階段首先對各,個Region 的回收價值和成本進行排序,根據用戶指望的GC 停頓時間來制定回收計劃。

相關文章
相關標籤/搜索