文章首發(fā)于公眾號《程序員果果》
成都創(chuàng)新互聯(lián)長期為上千客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為宜城企業(yè)提供專業(yè)的網(wǎng)站建設(shè)、網(wǎng)站設(shè)計,宜城網(wǎng)站改版等技術(shù)服務(wù)。擁有十載豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。地址 : https://mp.weixin.qq.com/s/gfdml-SfvhFdMXlAu-a61w
Java 11包含一個全新的垃圾收集器--ZGC,它由Oracle開發(fā),承諾在數(shù)TB的堆上具有非常低的暫停時間。 在本文中,我們將介紹開發(fā)新GC的動機,技術(shù)概述以及由ZGC開啟的一些可能性。
那么為什么需要新GC呢?畢竟Java 10已經(jīng)有四種發(fā)布多年的垃圾收集器,并且?guī)缀醵际菬o限可調(diào)的。 換個角度看,G1是2006年時引入Hotspot VM的。當(dāng)時大的AWS實例有1 vCPU和1.7GB內(nèi)存,而今天AWS很樂意租給你一個x1e.32xlarge實例,該類型實例有128個vCPU和3,904GB內(nèi)存。 ZGC的設(shè)計目標(biāo)是:支持TB級內(nèi)存容量,暫停時間低(<10ms),對整個程序吞吐量的影響小于15%。 將來還可以擴展實現(xiàn)機制,以支持不少令人興奮的功能,例如多層堆(即熱對象置于DRAM和冷對象置于NVMe閃存),或壓縮堆。
為了理解ZGC如何匹配現(xiàn)有收集器,以及如何實現(xiàn)新GC,我們需要先了解一些術(shù)語。最基本的垃圾收集涉及識別不再使用的內(nèi)存并使其可重用?,F(xiàn)代收集器在幾個階段進行這一過程,對于這些階段我們往往有如下描述:
并行:在JVM運行時,同時存在應(yīng)用程序線程和垃圾收集器線程。 并行階段是由多個gc線程執(zhí)行,即gc工作在它們之間分配。 不涉及GC線程是否需要暫停應(yīng)用程序線程。
串行:串行階段僅在單個gc線程上執(zhí)行。與之前一樣,它也沒有說明GC線程是否需要暫停應(yīng)用程序線程。
STW:STW階段,應(yīng)用程序線程被暫停,以便gc執(zhí)行其工作。 當(dāng)應(yīng)用程序因為GC暫停時,這通常是由于Stop The World階段。
并發(fā):如果一個階段是并發(fā)的,那么GC線程可以和應(yīng)用程序線程同時進行。 并發(fā)階段很復(fù)雜,因為它們需要在階段完成之前處理可能使工作無效。
現(xiàn)在我們了解了不同gc階段的屬性,讓我們繼續(xù)探討ZGC的工作原理。 為了實現(xiàn)其目標(biāo),ZGC給Hotspot Garbage Collectors增加了兩種新技術(shù):著色指針和讀屏障。
著色指針是一種將信息存儲在指針(或使用Java術(shù)語引用)中的技術(shù)。因為在64位平臺上(ZGC僅支持64位平臺),指針可以處理更多的內(nèi)存,因此可以使用一些位來存儲狀態(tài)。 ZGC將限制大支持4Tb堆(42-bits),那么會剩下22位可用,它目前使用了4位: finalizable, remap, mark0和mark1。 我們稍后解釋它們的用途。
著色指針的一個問題是,當(dāng)您需要取消著色時,它需要額外的工作(因為需要屏蔽信息位)。 像SPARC這樣的平臺有內(nèi)置硬件支持指針屏蔽所以不是問題,而對于x86平臺來說,ZGC團隊使用了簡潔的多重映射技巧。
要了解多重映射的工作原理,我們需要簡要解釋虛擬內(nèi)存和物理內(nèi)存之間的區(qū)別。 物理內(nèi)存是系統(tǒng)可用的實際內(nèi)存,通常是安裝的DRAM芯片的容量。 虛擬內(nèi)存是抽象的,這意味著應(yīng)用程序?qū)ΓㄍǔJ歉綦x的)物理內(nèi)存有自己的視圖。 操作系統(tǒng)負(fù)責(zé)維護虛擬內(nèi)存和物理內(nèi)存范圍之間的映射,它通過使用頁表和處理器的內(nèi)存管理單元(MMU)和轉(zhuǎn)換查找緩沖器(TLB)來實現(xiàn)這一點,后者轉(zhuǎn)換應(yīng)用程序請求的地址。
多重映射涉及將不同范圍的虛擬內(nèi)存映射到同一物理內(nèi)存。 由于設(shè)計中只有一個remap,mark0和mark1在任何時間點都可以為1,因此可以使用三個映射來完成此操作。 ZGC源代碼中有一個很好的圖表可以說明這一點。
讀屏障是每當(dāng)應(yīng)用程序線程從堆加載引用時運行的代碼片段(即訪問對象上的非原生字段non-primitive field):
void printName( Person person ) {
String name = person.name; // 這里觸發(fā)讀屏障
// 因為需要從heap讀取引用
//
System.out.println(name); // 這里沒有直接觸發(fā)讀屏障
}
在上面的代碼中,String name = person.name 訪問了堆上的person引用,然后將引用加載到本地的name變量。此時觸發(fā)讀屏障。 Systemt.out那行不會直接觸發(fā)讀屏障,因為沒有來自堆的引用加載(name是局部變量,因此沒有從堆加載引用)。 但是System和out,或者println內(nèi)部可能會觸發(fā)其他讀屏障。
這與其他GC使用的寫屏障形成對比,例如G1。讀屏障的工作是檢查引用的狀態(tài),并在將引用(或者甚至是不同的引用)返回給應(yīng)用程序之前執(zhí)行一些工作。 在ZGC中,它通過測試加載的引用來執(zhí)行此任務(wù),以查看是否設(shè)置了某些位。 如果通過了測試,則不執(zhí)行任何其他工作,如果失敗,則在將引用返回給應(yīng)用程序之前執(zhí)行某些特定于階段的任務(wù)。
現(xiàn)在我們了解了這兩種新技術(shù)是什么,讓我們來看看ZG的GC循環(huán)。
GC循環(huán)的第一部分是標(biāo)記。標(biāo)記包括查找和標(biāo)記運行中的應(yīng)用程序可以訪問的所有堆對象,換句話說,查找不是垃圾的對象。
ZGC的標(biāo)記分為三個階段。 第一階段是STW,其中GC roots被標(biāo)記為活對象。 GC roots類似于局部變量,通過它可以訪問堆上其他對象。 如果一個對象不能通過遍歷從roots開始的對象圖來訪問,那么應(yīng)用程序也就無法訪問它,則該對象被認(rèn)為是垃圾。從roots訪問的對象集合稱為Live集。GC roots標(biāo)記步驟非常短,因為roots的總數(shù)通常比較小。
該階段完成后,應(yīng)用程序恢復(fù)執(zhí)行,ZGC開始下一階段,該階段同時遍歷對象圖并標(biāo)記所有可訪問的對象。 在此階段期間,讀屏障針使用掩碼測試所有已加載的引用,該掩碼確定它們是否已標(biāo)記或尚未標(biāo)記,如果尚未標(biāo)記引用,則將其添加到隊列以進行標(biāo)記。
在遍歷完成之后,有一個最終的,時間很短的的Stop The World階段,這個階段處理一些邊緣情況(我們現(xiàn)在將它忽略),該階段完成之后標(biāo)記階段就完成了。
GC循環(huán)的下一個主要部分是重定位。重定位涉及移動活動對象以釋放部分堆內(nèi)存。 為什么要移動對象而不是填補空隙? 有些GC實際是這樣做的,但是它導(dǎo)致了一個不幸的后果,即分配內(nèi)存變得更加昂貴,因為當(dāng)需要分配內(nèi)存時,內(nèi)存分配器需要找到可以放置對象的空閑空間。 相比之下,如果可以釋放大塊內(nèi)存,那么分配內(nèi)存就很簡單,只需要將指針遞增新對象所需的內(nèi)存大小即可。
ZGC將堆分成許多頁面,在此階段開始時,它同時選擇一組需要重定位活動對象的頁面。選擇重定位集后,會出現(xiàn)一個Stop The World暫停,其中ZGC重定位該集合中root對象,并將他們的引用映射到新位置。與之前的Stop The World步驟一樣,此處涉及的暫停時間僅取決于root的數(shù)量以及重定位集的大小與對象的總活動集的比率,這通常相當(dāng)小。所以不像很多收集器那樣,暫停時間隨堆增加而增加。
移動root后,下一階段是并發(fā)重定位。 在此階段,GC線程遍歷重定位集并重新定位其包含的頁中所有對象。 如果應(yīng)用程序線程試圖在GC重新定位對象之前加載它們,那么應(yīng)用程序線程也可以重定位該對象,這可以通過讀屏障(在從堆加載引用時觸發(fā))實現(xiàn),如流程圖如下所示:
這可確保應(yīng)用程序看到的所有引用都已更新,并且應(yīng)用程序不可能同時對重定位的對象進行操作。
GC線程最終將對重定位集中的所有對象重定位,然而可能仍有引用指向這些對象的舊位置。 GC可以遍歷對象圖并重新映射這些引用到新位置,但是這一步代價很高昂。 因此這一步與下一個標(biāo)記階段合并在一起。在下一個GC周期的標(biāo)記階段遍歷對象對象圖的時候,如果發(fā)現(xiàn)未重映射的引用,則將其重新映射,然后標(biāo)記為活動狀態(tài)。
試圖單獨理解復(fù)雜垃圾收集器(如ZGC)的性能特征是很困難的,但從前面的部分可以清楚地看出,我們所碰到的幾乎所有暫停都只依賴于GC roots集合大小,而不是實時堆大小。標(biāo)記階段中處理標(biāo)記終止的最后一次暫停是唯一的例外,但是它是增量的,如果超過gc時間預(yù)算,那么GC將恢復(fù)到并發(fā)標(biāo)記,直到再次嘗試。
那ZGC到底表現(xiàn)如何?
Stefan Karlsson和Per Liden在今年早些時候的Jfokus演講中給出了一些數(shù)字。 ZGC的SPECjbb 2015吞吐量與Parallel GC(優(yōu)化吞吐量)大致相當(dāng),但平均暫停時間為1ms,最長為4ms。 與之相比G1和Parallel有很多次超過200ms的GC停頓。
然而,垃圾收集器是復(fù)雜的軟件,從基準(zhǔn)測試結(jié)果可能無法推測出真實世界的性能。我們期待自己測試ZGC,以了解它的性能如何因工作負(fù)載而異。
本文參考:https://mp.weixin.qq.com/s/nAjPKSj6rqB_eaqWtoJsgw
歡迎關(guān)注我的公眾號《程序員果果》,關(guān)注有驚喜~~
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。