JVM垃圾收集器
前言
最近被問到了JVM的垃圾收集器,在此進行整理記錄,供大家一起學習。
jvm垃圾收集器
jvm垃圾收集器是jvm記憶體回收的具體實現。本次討論hotspot虛擬機中的垃圾收集器。
圖中總共有7中垃圾收集器。兩個收集器中連線則表示可以配合使用。
中間的橫線代表新生代和老年代的分區。上面的是新生代的垃圾收集器,下面是老年代的垃圾收集器。
serial收集器
serial收集器是最早的收集器,是單執行緒的垃圾收集器,採用複製收集演算法。在他進行垃圾收集的時候,只會佔用一個cpu或一個執行緒。垃圾收集時,必須暫停所有的用戶執行緒,直到垃圾收集結束。
serial收集器的運行過程如下:
serial收集器是運行在client模式下的默認新生代收集器。簡單而高效。
ParNew收集器
ParNew收集器是Serial收集器的多執行緒版本。除了多執行緒外,其他與serial收集器一摸一樣。也是一款複製演算法的收集器。
ParNew收集器的運行過程如下:
ParNew收集器是server模式下的虛擬機的首選垃圾收集器。ParNew收集器在單CPU環境下,效果沒有serial收集器的效率高,還存在執行緒切換的開銷。默認開啟的執行緒數與CPU數量相等。可以使用-XX:ParallelGcThreads參數來限制收集的執行緒數。
Parallel Scavenge收集器
Parallel Scavenge收集器也採用複製演算法,特點是該收集器關注吞吐量。吞吐量就是CPU用於運行用戶程式碼的時間與CPU總消耗時間的比值。停頓時間越短就越適合與用戶交互的程式。高吞吐量可以高效率的利用CPU時間,儘快完成程式運算。適合在後台運算,不需要太多的交互任務。
Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量,分別是控制景大垃圾收集
停頓時間的-XX:MaxGCPauseMillis參數以及直接設置吞吐量大小的-XX:GCTimeRatio參數。
MaxGCPauseMillis參教允許的值是一個大於0的毫秒數,收集器將儘可能地保證記憶體回
收花費的時間不超過設定值。
不過大家不要認為如果把這個參數的值設置得很小,就能使
得系統的垃圾收集速度變得更快,GC停頓時間縮短是以犧牲吞吐量和新生代空間來換取的。
系統把新生代調小一點,收集300MB新生代肯定比收集500MB快,這也直接導致垃圾
收集發生得更頻繁一些,原來10秒收集一次、每次停頓100毫秒,現在變成5秒收集一次、
每次停頓70毫秒。
停頓時間的確在下降,但吞吐量也降下來了。
GCTimeRatio參數的值應當是一個大於0且小於100的整數,也就是垃圾收集時間佔總
時間的比率,相當於是吞吐量的倒數。如果把此參數設置為19,那允許的最大GC時間就占
總時間的5% (即1/<1+19)>,默認值為99。就是允許最大1%(即1 / (1+99))的垃圾收
集時間。
由於與吞吐量關係密切,Parallel Scavenge收集器也經常稱為吞吐量優先收集器。
除上述兩個參數之外,Parallel Scavenge收集器還有一個參數-XX:+UscAdaptiveSizePolicy
值得關注。這是一個開關參數,當這個參數打開之後,就不需要手工指定新生代的大
小-Xmn, Eden與Survivor區的比例(-XX:SurvivorRatio)、晉陞老年代對象年齡
(-XX:pretenureSizeThreshold)等細節參數了,虛擬機會根據當前系統的運行情況收集性能監控資訊,動態調整參數。這種調節方式為自適應調節策略。
Serial Old收集器
Serial Old是Serial收集器的老年代版本,它同樣是一個單執行緒收集器,使用「標記一
整理」演算法。這個收集器的主要意義也是在於給Client模式下的虛擬機使用。如果在Server
模式下,那麼它主要還有兩大用途:一種用途是在JDK1.5以及之前的版本中與Parallel
Scavenge收集器搭配使用。另一種用途就是作為CMS收集器的後備預案,在並發收集發生
Concurrent Mode Failure時使用。這兩點都將在後面的內容中詳細講解。
Serial Old收集器的運行過程如下:
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多執行緒和「標記一整理」算
法。這個收集器是在JDK1.6中才開始提供的。
Parallel Old的運行過程如下
Cms收集器
CMS收集器是一種以獲取最短回收停頓時間為目標的收集器。CMS收集器是基於”標記一清除」演算法實現的。
運行過程分為四個步驟:
1、初始標記
2、並發標記
3、重新標記
4、並發清除
其中初始標記、重新標記這兩個步驟仍是-Stop The World。初始標記只是標記GC Roots能直接關聯到的對象,速度很快;
並發標記階段進行GC Roots tracing的過程。而重新標記階段則是為了修正並發標記期間因用戶程式繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短。由於整個過程中耗時最長的並發標記和並發清除過程收集器執行緒都可以與用戶執行緒一起工作,所以,從總體上來說,CMS收集器的記憶體回收過程是與用戶執行緒一起並發執行的。
運行過程如下:
Cms有幾個明顯的缺點:
1、Cms收集器對cpu資源異常敏感。
2、Cms收集器無法處理浮動垃圾,可能出現Concurrent mode failure失敗導致另一次gc的產生。
3、Cms標記清楚會產生記憶體垃圾。
G1收集器
G1是一款面向服務端應用的垃圾收集器。G1收集器具備如下特點。
1、並行與並發,G1能充分利用多CPU、多核環境下的硬體優勢,使用多個CPU (CPU或者CPU核心)來縮短Stop The World停頓的時間,部分其他收集器原本需要停頓Java執行緒執行的GC動作,G1收集器仍然可以通過並發的方式讓Java程式繼續執行。
2、分代收集:與其他收集器一樣,分代概念在G1中依然得以保留。雖然G1可以不需要其他收集器配合就能獨立管理整個GC堆,但它能夠釆用不同的方式去處理新創建的對象和已經存活了一段時間、熬過多次GC的舊對象以獲取更好的收集效果。
3、空間整合:與CMS的「標記一清理」演算法不同,G1從整體來看是基於「標記一整理」演算法實現的收集器,從局部(兩個Region之間)上來看是基於「複製」演算法實現的,但無論如何,這兩種演算法都意味著G1運作期間不會產生記憶體空間碎片,收集後能提供規整的可用記憶體。這種特性有利於程式長時間運行,分配大對象時不會因為無法找到連續記憶體空間而提前觸發下一次GC。
4、可預測的停頓:這是G1相對於CMS的另一大優勢,降低停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度為N毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒,這幾乎已經是實時Java(RTSJ)的垃圾收集器的特徵了。
在G1之前的其他收集器進行收集的範圍都是整個新生代或者老年代,而G1不再是這樣。使用G1收集器時,Java堆的記憶體布局就與其他收集器有很大差別,它將整個Java堆劃
分為多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了,它們都是一部分Region (不需要連續)的集合。
G1收集器之所以能建立可預測的停頓時間模型,是因為它可以有計劃地避免在整個Java堆中進行全區域的垃圾收集。
G1跟蹤各個Region裡面的垃圾堆積的價值大小,在後台維護一個優先列表,每次根據允許
的收集時間,優先回收價值最大的Region (這也就是Garbage-First名稱的來由)。這種使用
Region劃分記憶體空間以及有優先順序的區域回收方式,保證了G1收集器在有限的時間內可以
獲取儘可能高的收集效率。
G1收集器的運作過程如下:
1、初始標記
2、並發標記
3、最終標記
4、篩選回收