1. 程式人生 > >jvm垃圾收集器之Throughput GC

jvm垃圾收集器之Throughput GC

sep ash http 不錯 而已 代碼 是我 ont dap

呃。HotSpot VM的GC組老人之一Jon Masamitsu很久之前就寫過blog講解這個:https://blogs.oracle.com/jonthecollector/entry/our_collectors

簡單來說,有這麽多東西反映了HotSpot VM的開發歷史和實現細節。我在寫篇東西講述這部分歷史,哪天寫完的話在這邊也放個鏈接嗯。

DefNewGeneration是default new generation
ParNewGeneration是parallel new generation

原本HotSpot VM裏沒有並行GC,當時就只有NewGeneration;後來準備要加入young gen的並行GC,就把原本的NewGeneration改名為DefNewGeneration,然後把新加的並行版叫做ParNewGeneration。


這些XXXGeneration都在HotSpot VM的“分代式GC框架”內。本來HotSpot VM鼓勵開發者盡量在這個框架內開發GC,但後來有個開發就是不願意被這框架憋著,自己硬寫了個沒有使用已有框架的新並行GC,並拉攏性能測試團隊用這個並行GC來跑分,成績也還不錯,於是這個GC就放進HotSpot VM裏了。這就是我們現在看到的ParallelScavenge。

(結果就是HotSpot GC組不得不維護兩個功能幾乎一樣、但各種具體細節不同的並行GC。其實是件很頭疼的事情嗯)

Scavenge或者叫scavenging GC,其實就是copying GC的另一種叫法而已。HotSpot VM裏的GC都是在minor GC收集器裏用scavenging的,DefNew、ParNew和ParallelScavenge都是,只不過DefNew是串行的copying GC,而後兩者是並行的copying GC。

由此名字就可以知道,“ParallelScavenge”的初衷就是把“scavenge”給並行化。換句話說就是把minor GC並行化。至於full GC,那不是當初關註的重點。

把GC並行化的目的是想提高GC速度,也就是提高吞吐量(throughput)。所以其實ParNew與ParallelScavenge都可叫做Throughput GC。
但是在HotSpot VM的術語裏“Throughput GC”通常特指“ParallelScavenge”。

================================

ParallelScavenge和ParNew都是並行GC,主要是並行收集young gen,目的和性能其實都差不多。最明顯的區別有下面幾點:
1、PS以前是廣度優先順序來遍歷對象圖的,JDK6的時候改為默認用深度優先順序遍歷,並留有一個UseDepthFirstScavengeOrder參數來選擇是用深度還是廣度優先。在JDK6u18之後這個參數被去掉,PS變為只用深度優先遍歷。ParNew則是一直都只用廣度優先順序來遍歷
2、PS完整實現了adaptive size policy,而ParNew及“分代式GC框架”內的其它GC都沒有實現(倒不是不能實現,就是麻煩+沒人力資源去做)。所以千萬千萬別在用ParNew+CMS的組合下用UseAdaptiveSizePolicy,請只在使用UseParallelGC或UseParallelOldGC的時候用它。
3、由於在“分代式GC框架”內,ParNew可以跟CMS搭配使用,而ParallelScavenge不能。當時ParNew GC被從Exact VM移植到HotSpot VM的最大原因就是為了跟CMS搭配使用。
4、在PS成為主要的throughput GC之後,它還實現了針對NUMA的優化;而ParNew一直沒有得到NUMA優化的實現。

================================

還有一點要註意:上面說ParallelScavenge並行收集young gen,那old/perm gen呢?

其實最初的ParallelScavenge的目標只是並行收集young gen,而full GC的實際實現還是跟serial GC一樣。只不過因為它沒有用HotSpot VM的generational GC framework,自己實現了一個CollectedHeap的子類ParallelScavengeHeap,裏面都弄了獨立的一套接口,而跟HotSpot當時其它幾個GC不兼容。其實真的有用的代碼大部分就在PSScavenge(=“ParallelScavenge的Scavenge”)裏,也就是負責minor GC的收集器;而負責full GC的收集器叫做PSMarkSweep(=“ParallelScavenge的MarkSweep”),其實只是在serial GC的核心外面套了層皮而已,骨子裏是一樣的LISP2算法的mark-compact收集器(別被名字騙了,它並不是一個mark-sweep收集器)。

當啟用-XX:+UseParallelGC時,用的就是PSScavenge+PSMarkSweep的組合。
這是名副其實的“ParallelScavenge”——只並行化了“scavenge”。

所以其實非要說對應關系的話,PSScavenge才是真的跟ParNew對等的東西;ParallelScavenge這個名字既指代整套新GC,也可指代其真正賣點的PSScavenge。

不知道後來什麽原因導致full GC的並行化並沒有在原本的generational GC framework上進行,而只在ParallelScavenge系上進行了。其成果就是使用了LISP2算法的並行版的full GC收集器,名為PSCompact(=“ParallelScavenge-MarkCompact”),收集整個GC堆。

當啟用-XX:+UseParallelOldGC時,用的就是PSScavenge+PSCompact的組合。
此時ParallelScavenge其實已經名不符實了——它不只並行化了“scavenge”(minor GC),也並行化了“mark-compact”(full GC)。

jvm垃圾收集器之Throughput GC