Z Garbage Collector浪规,即ZGC探孝,是一個可伸縮的顿颅、低延遲的垃圾收集器,主要為了滿足如下目標進行設計:
- 停頓時間不會超過10ms
- 停頓時間不會隨著堆的增大而增大(不管多大的堆都能保持在10ms以下)
- 可支持幾百M庇配,甚至幾T的堆大小(最大支持4T)
停頓時間在10ms以下耀鸦,10ms其實是一個很保守的數(shù)據(jù)啸澡,在SPECjbb 2015基準測試嗅虏,128G的大堆下最大停頓時間才1.68ms,遠低于10ms吏口,和G1算法相比冰更,也感覺像是在虐菜蜀细。
G1算法通過只回收部分Region奠衔,避免了全堆掃描,改善了大堆下的停頓時間痊夭,但在普通大小的堆里卻表現(xiàn)平平她我,ZGC為什么可以這么優(yōu)秀迫横,主要是因為以下幾個特性矾踱。
Concurrent
ZGC只有短暫的STW,大部分的過程都是和應用線程并發(fā)執(zhí)行禾怠,比如最耗時的并發(fā)標記和并發(fā)移動過程。
Region-based
ZGC中沒有新生代和老年代的概念衡瓶,只有一塊一塊的內存區(qū)域page牲证,以page單位進行對象的分配和回收。
Compacting
每次進行GC時十厢,都會對page進行壓縮操作蛮放,所以完全避免了CMS算法中的碎片化問題包颁。
NUMA-aware
現(xiàn)在多CPU插槽的服務器都是Numa架構压真,比如兩顆CPU插槽(24核),64G內存的服務器岳悟,那其中一顆CPU上的12個核贵少,訪問從屬于它的32G本地內存堆缘,要比訪問另外32G遠端內存要快得多。
ZGC默認支持NUMA架構录平,在創(chuàng)建對象時萄涯,根據(jù)當前線程在哪個CPU執(zhí)行绪氛,優(yōu)先在靠近這個CPU的內存進行分配,這樣可以顯著的提高性能争占,在SPEC JBB 2005 基準測試里獲得40%的提升。
Using colored pointers
和以往的標記算法比較不同伯襟,CMS和G1會在對象的對象頭進行標記姆怪,而ZGC是標記對象的指針稽揭。
其中低42位對象的地址肥卡,42-45位用來做指標標記步鉴。
Using load barriers
因為在標記和移動過程中氛琢,GC線程和應用線程是并發(fā)執(zhí)行的,所以存在這種情況:對象A內部的引用所指的對象B在標記或者移動狀態(tài)册舞,為了保證應用線程拿到的B對象是對的调鲸,那么在讀取B的指針時會經過一個 “l(fā)oad barriers” 讀屏障挽荡,這個屏障可以保證在執(zhí)行GC時定拟,數(shù)據(jù)讀取的正確性青自。
一些變化
JDK11
- ZGC的最初版本
- 不支持類卸載class unloading (using -XX:+ClassUnloading has no effect)
JDK12 - 進一步減少停頓時間
- 支持類卸載功能
平臺支持
ZGC目前只在Linux/x64上可用恋腕,如果有足夠的需求,將來可能會增加對其他平臺的支持荠藤。
目前只支持64位的linux系統(tǒng)哈肖,狼哥在mac跑了半天都是下面的錯!
如何編譯
$ hg clone https://wiki.openjdk.java.net/display/hg.openjdk.java.net/jdk/jdk
$ cd jdk
$ sh configure
$ make images
如果正在編譯的版本是 11.0.0, 11.0.1 or 11.0.2,必須加上配置參數(shù)--with-jvm-features=zgc
開啟ZGC的編譯币狠,在11.0.3或者12之后,可以忽略這個參數(shù)总寻,已經默認支持器罐。
編譯結束之后,你會得到一個完整的JDK渐行,在Linux中轰坊,可以在下面目錄中找到這個新的JDK
./build/linux-x86_64-normal-server-release/images/jdk
可以進入bin文件夾,執(zhí)行 ./java -version
驗證一下祟印。
如何使用
編譯完成之后肴沫,已經迫不及待的想試試ZGC,需要配置以下JVM參數(shù)蕴忆,才能使用ZGC.
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xmx10g -Xlog:gc
參數(shù)說明:
Heap Size
通過-Xmx10g
進行設置颤芬。
-Xmx是ZGC收集器中最重要的調優(yōu)選項,大大解決了程序員在JVM參數(shù)調優(yōu)上的困擾套鹅。ZGC是一個并發(fā)收集器站蝠,必須要設置一個最大堆的大小,應用需要多大的堆菱魔,主要有下面幾個考量:
- 對象的分配速率杰妓,要保證在GC的時候桩卵,堆中有足夠的內存分配新對象
- 一般來說晴叨,給ZGC的內存越多越好件蚕,但是也不能浪費內存,所以要找到一個平衡妄痪。
Concurrent GC Threads
通過-XX:ConcGCThread = 4
進行設置土浸。
并發(fā)執(zhí)行的GC線程數(shù)泪酱,如果沒有設置岂津,在JVM啟動的時候會根據(jù)CPU的核數(shù)計算出一個合理的數(shù)量,默認是核數(shù)的12.5%,但是根據(jù)應用的特性茶宵,可以通過手動設置調整种蝶。
因為在并發(fā)標記和并發(fā)移動時透敌,GC線程和應用線程是并發(fā)執(zhí)行的魄藕,所以存在搶占CPU的情況,對于一些對延遲比較敏感的應用,這個并發(fā)線程數(shù)就不能設置的過大,不然會降低應用的吞吐量翻翩,并有可能增加應用的延遲桨仿,因為GC線程占用了太多的CPU骂铁,但是如果設置的太小,就有可能對象的分配速率比垃圾收集的速率來的大操刀,最終導致應用線程停下來等GC線程完成垃圾收集窃蹋,并釋放內存振湾。
一般來說,如果低延遲對應用程序很重要,那么不要這個值不要設置的過于大,理想情況下排嫌,系統(tǒng)的CPU利用率不應該超過70%颇象。
Parallel GC Threads
通過-XX:ParallelGCThreads = 20
當對GC Roots進行標記和移動時阅爽,需要進行STW砰识,這個過程會使用ParallelGCThreads個GC線程進行并行執(zhí)行膨处。
ParallelGCThreads默認為CPU核數(shù)的60%突硝,為什么可以這么大修噪?
因為這個時候,應用線程已經完全停下來了,所以要用盡可能多的線程完成這部分任務,這樣才能讓STW盡可能的短暫。