日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

JDK之ZGC介绍

發(fā)布時(shí)間:2024/1/23 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JDK之ZGC介绍 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

**視頻課:https://edu.csdn.net/course/detail/31331
前言

ZGC是最近由Oracle為OpenJDK開源的新垃圾收集器。它主要由Per Liden編寫。ZGC類似于Shenandoah或Azul的C4,專注于減少暫停時(shí)間的同時(shí)仍然壓縮堆 。

雖然我不會(huì)在這里給出完整的介紹,但“壓縮堆”只是意味著將仍然存活的對(duì)象移動(dòng)到堆的其他區(qū)域.這樣做有助于減少碎片,但通常這也意味著整個(gè)應(yīng)用程序(包括其所有線程)需要暫停,這通常被稱為Stop the world 。只有GC完成后,才能恢復(fù)應(yīng)用程序。

在GC相關(guān)的文獻(xiàn)中,應(yīng)用程序通常稱為mutator ,因?yàn)閺腉C的角度來(lái)看,應(yīng)用程序會(huì)改變堆(mutates the heap)。根據(jù)堆的大小,這樣的暫??赡苄枰獛酌腌?#xff0c;這對(duì)于交互式應(yīng)用程序來(lái)說(shuō)可能是難以接受的。

有幾種方法可以減少暫停時(shí)間:

GC可以在壓縮時(shí)使用多個(gè)線程(并行壓縮 parallel compaction) 壓縮工作也可以分為多個(gè)暫停(增量壓縮 incremental compaction) 壓縮堆的同時(shí)不暫停應(yīng)用程序,或者只是很短時(shí)間暫停(并發(fā)壓縮 concurrent compaction) Go的GC就是完全不壓縮堆

如前所述,ZGC會(huì)進(jìn)行并發(fā)壓縮,這當(dāng)然不是一個(gè)簡(jiǎn)單的實(shí)現(xiàn)功能,因此我想描述一下這是如何工作的。為什么這很復(fù)雜?

你需要將對(duì)象復(fù)制到另一個(gè)內(nèi)存地址,同時(shí)另一個(gè)線程仍然可以讀寫舊對(duì)象。

如果對(duì)象已經(jīng)復(fù)制成功,那么堆中仍有許多指向舊地址的引用需要更新到新地址。

雖然并發(fā)壓縮(concurrent compaction)似乎是上述方案中降低暫停時(shí)間的最佳解決方案,但肯定會(huì)涉及一些權(quán)衡。因此,如果您不關(guān)心暫停時(shí)間,那么最好使用專注于吞吐量的GC。
GC屏障 (GC Barriers)

理解ZGC如何進(jìn)行并發(fā)壓縮的關(guān)鍵是Load barrier (通常在GC文獻(xiàn)中稱為Read barrier).這里簡(jiǎn)單介紹一下,詳細(xì)的描述請(qǐng)看下面的Load Barrier一節(jié)。

如果GC有讀取屏障(Load barrier),則在從堆讀取引用時(shí),GC需要執(zhí)行一些額外操作。在Java中,也就是像執(zhí)行這樣的代碼Object xxx=obj.field時(shí)需要額外操作。

對(duì)于像obj.field = value這樣的操作,GC也可能需要寫入屏障(叫做Write Barrier或者Store Barrier)[譯注:在分代GC還有引用計(jì)數(shù)中會(huì)用到寫入屏障].

這兩個(gè)操作都比較特殊因?yàn)樗鼈冊(cè)诿看巫x取或?qū)懭攵褧r(shí)發(fā)生的。Load Barrier和Store Barrier的名稱有點(diǎn)令人困惑,但注意這個(gè)屏障與CPU的內(nèi)存障礙是完全不同的兩個(gè)概念

堆中的讀取和寫入都非常常見,因此兩種GC屏障都需要非常高效,在常見情況下就是一些匯編代碼。Read barrier通常比Write Barrier大一個(gè)數(shù)量級(jí)(可能會(huì)因應(yīng)用程序而異),因此Read Barrier對(duì)性能要求更高。

例如,分代GC通常只需要一個(gè)寫屏障,不需要讀屏障。ZGC則需要一個(gè)讀屏障但沒(méi)有寫屏障。對(duì)于并發(fā)壓縮,我沒(méi)有看到?jīng)]有讀取障礙的解決方案。

這里需要注意:即使GC需要某種類型的屏障,只有在讀取或?qū)懭攵阎械囊脮r(shí)需要它們。讀取或?qū)懭胂駃nt或double這樣的基本類型是不需要屏障的.
指針標(biāo)記(Pointer tagging Or Colored Pointers )

ZGC在堆引用中存儲(chǔ)額外的元數(shù)據(jù) ,在x64上是64 bit(ZGC目前不支持compressed oops和 class pointers)。64位中的48位用做x64上的虛擬內(nèi)存地址 。雖然確切地說(shuō)只有47位,因?yàn)榈?7位確定了位48-63的值(目前這些位都是0)。ZGC保留對(duì)象實(shí)際地址的前42位(在源代碼中稱為偏移量 )。42位地址理論上就會(huì)有4TB的堆大小限制。其余的位用于這些標(biāo)志: finalizable , remapped , marked1和marked0 (保留一位用于將來(lái)使用)。如下圖所示:

6 4 4 4 4 4 0
3 7 6 5 2 1 0
±------------------±±—±----------------------------------------------+
|00000000 00000000 0|0|1111|11 11111111 11111111 11111111 11111111 11111111|
±------------------±±—±----------------------------------------------+
| | | |
| | | * 41-0 Object Offset (42-bits, 4TB address space)
| | |
| | * 45-42 Metadata Bits (4-bits) 0001 = Marked0
| | 0010 = Marked1
| | 0100 = Remapped
| | 1000 = Finalizable
| |
| * 46-46 Unused (1-bit, always zero)
|

  • 63-47 Fixed (17-bits, always zero)

在堆引用中具有元數(shù)據(jù)信息使得解引用更加昂貴,因?yàn)樾枰猰ask地址以獲得沒(méi)有元信息的真實(shí)地址。ZGC采用了一個(gè)很好的技巧來(lái)避免這種情況:

當(dāng)從內(nèi)存中讀取時(shí),會(huì)設(shè)置marked0 , marked1或remapped中的一個(gè)。

在偏移x處分配頁(yè)面(allocating a page)時(shí),ZGC將同一頁(yè)面映射到3個(gè)不同的地址 :

for marked0 :(0b0001 << 42) | x for marked1 : (0b0010 << 42) | x for remapped : (0b0100 << 42) | x

因此,ZGC從地址4TB開始保留16TB的地址空間(但實(shí)際上并未使用所有這些內(nèi)存)。如下圖:

±-------------------------------+ 0x0000140000000000 (20TB)
| Remapped View |
±-------------------------------+ 0x0000100000000000 (16TB)
| (Reserved, but unused) |
±-------------------------------+ 0x00000c0000000000 (12TB)
| Marked1 View |
±-------------------------------+ 0x0000080000000000 (8TB)
| Marked0 View |
±-------------------------------+ 0x0000040000000000 (4TB)

在任何時(shí)間點(diǎn),只使用這三個(gè)視圖中的一個(gè)。調(diào)試時(shí)可以取消映射(unmapped)未使用的視圖來(lái)驗(yàn)證正確性。
Pages & Physical & Virtual Memory

Shenandoah將堆分成大量同樣大小的區(qū)域 。除了不適合單個(gè)區(qū)域的大對(duì)象外,對(duì)象通常不會(huì)跨越多個(gè)區(qū)域。大對(duì)象被分配在多個(gè)連續(xù)區(qū)域中。我非常喜歡這種方法,因?yàn)樗浅:?jiǎn)單。

在這方面,ZGC與Shenandoah非常相似。在ZGC的說(shuō)法中,區(qū)域稱為頁(yè)面Pages 。

與Shenandoah的主要區(qū)別:ZGC中的頁(yè)面可以有不同的大小(但在x64上總是2MB的倍數(shù))。

ZGC有3種不同的頁(yè)面類型: 小型 (2MB大小), 中型 (32MB大小)和大型 (2MB的倍數(shù))。

在小頁(yè)面中分配小對(duì)象(最大256KB大小),在中型頁(yè)面中分配中型對(duì)象(最多4MB)。大頁(yè)面中分配大于4MB的對(duì)象。大頁(yè)面只能存儲(chǔ)一個(gè)對(duì)象.小頁(yè)面或中間頁(yè)面可以分配多個(gè)。

有些令人困惑的是大頁(yè)面實(shí)際上可能小于中等頁(yè)面(例如,對(duì)于大小為6MB的大對(duì)象)。

ZGC的另一個(gè)不錯(cuò)的特性是,它還可以區(qū)分物理內(nèi)存和虛擬內(nèi)存。這背后的想法是通常有足夠的虛擬內(nèi)存(ZGC總是4TB),而物理內(nèi)存更稀缺。物理內(nèi)存可以擴(kuò)展到最大堆大小(使用-Xmx設(shè)置),因此這比4 TB的虛擬內(nèi)存要小得多。在ZGC中分配特定大小的頁(yè)面意味著分配物理和虛擬內(nèi)存。在ZGC中,物理內(nèi)存不需要是連續(xù)的,虛擬內(nèi)存空間是連續(xù)的。

為什么說(shuō)這是一個(gè)不錯(cuò)的屬性?

分配連續(xù)范圍的虛擬內(nèi)存是很容易的,因?yàn)槲覀兺ǔS凶銐虻奶摂M內(nèi)存。但在物理內(nèi)存中有3個(gè)大小為2MB的空閑頁(yè)面的情況很普通,但是對(duì)于大型對(duì)象分配我們需要6MB的連續(xù)內(nèi)存。有足夠的空閑物理內(nèi)存,但不幸的是這個(gè)內(nèi)存是不連續(xù)的。ZGC能夠?qū)⑦@些非連續(xù)的物理頁(yè)面映射到單個(gè)連續(xù)的虛擬內(nèi)存空間。如果無(wú)法映射,我們就會(huì)耗盡內(nèi)存(發(fā)生OOM)
標(biāo)記和重新安置對(duì)象(Marking & Relocating objects)

垃圾回收主要分為兩個(gè)階段:標(biāo)記和重新安置(實(shí)際上不止這兩個(gè)階段,你可以查閱源碼)。

[譯注:重新安置(Relocating)指的是把對(duì)象從一個(gè)內(nèi)存區(qū)域移到另外一個(gè)區(qū)域,重映射(Remapping)只的是把指向老的地址的引用更新到新的地址]

一次GC從標(biāo)記階段開始,標(biāo)記所有可到達(dá)的對(duì)象。在這個(gè)階段結(jié)束時(shí),我們知道哪些對(duì)象仍然存活,哪些對(duì)象是垃圾。ZGC將此信息存儲(chǔ)在每個(gè)頁(yè)面的Live Map中。Live Map是一個(gè)位圖(bitmap) ,用于存儲(chǔ)給定索引處的對(duì)象是否可達(dá)和/或最終可達(dá)(對(duì)于具有finalize method的對(duì)象而言)。

在標(biāo)記階段,應(yīng)用程序線程中的load-barrier將未標(biāo)記的引用推送到線程局部標(biāo)記緩沖區(qū)。只要此緩沖區(qū)已滿,GC線程就可以獲得此緩沖區(qū)的所有權(quán),并以遞歸方式遍歷此緩沖區(qū)中的所有可到達(dá)對(duì)象。在應(yīng)用程序線程中標(biāo)記只是將引用推送到緩沖區(qū),GC線程負(fù)責(zé)遍歷對(duì)象圖并更新Live map.

標(biāo)記階段結(jié)束后,ZGC要重新安置 Relocation set中的所有活動(dòng)對(duì)象。

Relocation Set表示一組需要被回收的頁(yè)面(Pages),例如那些垃圾最多的頁(yè)面。存活的對(duì)象由GC線程或應(yīng)用程序線程通過(guò)讀取屏障(Load Barrier)重新安置(relocated)(也就是放到新的地址去).ZGC為Relocation set中的每個(gè)頁(yè)面分配Forwarding table.

Forwarding table基本上是一個(gè)hash map,它存儲(chǔ)一個(gè)對(duì)象已被重新安置到的地址(如果該對(duì)象已經(jīng)被重新安置)。

ZGC方法的優(yōu)點(diǎn)是我們只需要為relocation set中的頁(yè)面分配forwarding table的空間.
相比之下,Shenandoah將轉(zhuǎn)發(fā)指針存儲(chǔ)在每個(gè)對(duì)象本身,這樣就誰(shuí)有一些額外的內(nèi)存開銷。

GC線程遍歷 Relocation set中的存活對(duì)象,并重新安置(relocate)尚未重新安置的對(duì)象。這時(shí)可能發(fā)生應(yīng)用程序線程和GC線程同時(shí)重新安置(relocate)同一個(gè)對(duì)象,在這種情況下,誰(shuí)先relocate誰(shuí)獲勝,ZGC使用原子CAS操作來(lái)確定勝者。

當(dāng)不處于marking階段時(shí),load-barrier會(huì)重新安置(relocates )/重新映射(remaps )從堆加載的所有引用。這確保了mutator看到的每個(gè)新引用都已指向?qū)ο蟮淖钚赂北?。重新映?#xff08;remaps)對(duì)象就是在forwarding table中查找新的對(duì)象地址。

一旦GC線程完成了relocation set的處理,重新安置階段就完成了。雖然這意味著所有對(duì)象都已重新安置,但通常仍會(huì)有引用指向relocation set,需要將其重新映射(remapped )到新地址。這些引用會(huì)被Load-Barrier自我修復(fù)。如果對(duì)于這些引用的讀取發(fā)生的不夠快,(也就是這段時(shí)間內(nèi),應(yīng)用程序沒(méi)有讀到這些指向relocation set的引用),這些引用會(huì)在下一次mark階段給修復(fù)。這意味著標(biāo)記階段還需要檢查 forward table以重新映射(remap) (但不重新安置 ,所有對(duì)象之前階段都保證被重新安置)對(duì)象到它們的新地址。

這也解釋了為什么對(duì)象引用中有兩個(gè)標(biāo)記位(marked0 和marked1 )。標(biāo)記階段在標(biāo)記的marked0和marked1位之間交替。在重新安置階段之后,仍可能存在未重定向(remapped)的引用,所以我們需要知道上一個(gè)gc周期的情況。如果新的標(biāo)記階段使用相同的標(biāo)記位,則Load-Barrier就知道該引用為已標(biāo)記。

(譯注:這里看起來(lái)像是GC周期remap和mark可以重疊,實(shí)際上確實(shí)是重疊的。如圖所示:
gc phase
更詳細(xì)的信息可以看這個(gè)Slide)
Load-Barrier

從堆中讀取引用時(shí),ZGC需要一個(gè)所謂的load-barrier(也稱為read-barrier)。每次Java程序訪問(wèn)對(duì)象類型的字段時(shí),我們都需要插入此load-barrier,例如obj.field 。訪問(wèn)某些其他原始類型的字段不需要屏障,例如obj.anInt或obj.anDouble 。ZGC不需要obj.field = someValue存儲(chǔ)/寫入障礙。

根據(jù)GC當(dāng)前所處的階段(存儲(chǔ)在全局變量ZGlobalPhase中 ),如果尚未標(biāo)記或重新安置對(duì)象,則屏障會(huì)標(biāo)記對(duì)象或重新安置它

全局變量ZAddressGoodMask和ZAddressBadMask
存儲(chǔ)對(duì)應(yīng)的掩碼,該掩碼確定引用是否已被認(rèn)為是好的(這意味著已經(jīng)標(biāo)記或重新映射/重新安置remapped/relocated)或者是否仍然需要一些操作。這些變量?jī)H在標(biāo)記開始階段和重新安置階段同時(shí)改變.ZGC源代碼中的這個(gè)表格可以很好地概述這些掩碼的狀態(tài):

GoodMask BadMask WeakGoodMask WeakBadMask--------------------------------------------------------------

Marked0 001 110 101 010
Marked1 010 101 110 001
Remapped 100 011 100 011

屏障的匯編代碼可以在MacroAssembler for x64中看到,我只會(huì)為這個(gè)屏障顯示一些偽匯編代碼:

mov rax, [r10 + some_field_offset]
test rax, [address of ZAddressBadMask]
jnz load_barrier_mark_or_relocate

otherwise reference in rax is considered good

第一個(gè)匯編指令從堆讀取引用: r10存儲(chǔ)對(duì)象引用, some_field_offset是一些字段偏移常量。加載的引用存儲(chǔ)在rax寄存器中。

然后針對(duì)當(dāng)前的壞掩碼測(cè)試該引用(這只是一個(gè)位與)。此處不需要同步,因?yàn)閆AddressBadMask僅在STW時(shí)才更新。如果結(jié)果不為零,我們需要執(zhí)行屏障。

屏障需要根據(jù)我們當(dāng)前所處的GC階段標(biāo)記或重新安置對(duì)象。在此操作之后, 他需要更新存儲(chǔ)在r10 + some_field_offset中的引用來(lái)指向新引用。這步操作是必要的,以便來(lái)該字段的后續(xù)加載返回正確的引用。

由于我們可能需要更新引用地址,因此我們需要使用兩個(gè)寄存器r10和rax作為加載的引用和對(duì)象地址。正確的引用也需要存儲(chǔ)到寄存器rax中 ,這樣在后面的執(zhí)行過(guò)程中我們就已經(jīng)加載了正確的引用。

由于每個(gè)引用都需要標(biāo)記或重新安置,因此在開始標(biāo)記或重新安置階段后,吞吐量可能會(huì)立即降低。當(dāng)大多數(shù)引用被修復(fù)時(shí),這應(yīng)該會(huì)變得更快。
Stop-the-World 停頓

ZGC并沒(méi)有徹底擺脫STW。收集器在開始標(biāo)記,結(jié)束標(biāo)記和開始重新安置時(shí)需要暫停。但這種暫停通常很短,只有幾毫秒。

當(dāng)開始標(biāo)記時(shí),ZGC遍歷所有線程堆棧以標(biāo)記root set。root set是遍歷對(duì)象圖的開始的地方。root set通常由本地和全局變量組成,但也包括其他內(nèi)部VM結(jié)構(gòu)(例如JNI句柄)。

結(jié)束標(biāo)記階段時(shí)需要再次暫停。在此暫停中,GC需要清空并遍歷所有線程局部標(biāo)記緩沖區(qū)。由于GC可能會(huì)發(fā)現(xiàn)一個(gè)未標(biāo)記的大型子圖,因此可能需要更長(zhǎng)時(shí)間。ZGC試圖通過(guò)在1毫秒后停止標(biāo)記階段的結(jié)束來(lái)避免這種情況。它返回到并發(fā)標(biāo)記階段,直到遍歷整個(gè)對(duì)象圖,然后可以再次開始結(jié)束標(biāo)記階段

啟動(dòng)重新安置階段會(huì)再次暫停應(yīng)用程序。此階段與開始標(biāo)記非常相似,不同之處在于此階段重新安置Root Set中的對(duì)象。
zgc是一款可拓展的低時(shí)延,為實(shí)現(xiàn)以下幾個(gè)目標(biāo)而誕生的垃圾回收器:

停頓時(shí)間不超過(guò)10ms 停頓時(shí)間不會(huì)因堆變大而變長(zhǎng) 堆大小范圍可支持幾G到幾T

再看一下zgc的標(biāo)簽:

region-based (和G1一樣) NUMA-aware Concurrent Compacting Using load barriers(讓一個(gè)CPU處理單元中的內(nèi)存狀態(tài)對(duì)其它處理單元可見的一項(xiàng)技術(shù),java的volatile底層使用的就是load barrier) Using colored pointers()

zgc介紹-by hotspot garbage collector team:https://archive.fosdem.org/2018/schedule/event/zgc/attachments/slides/2211/export/events/attachments/zgc/slides/2211/ZGC_FOSDEM_2018.pdf
一、zgc在jdk各個(gè)版本的changelog:

JDK 13 (Released September 2019)

Increased max heap size from 4TB to 16TB Support for uncommitting unused memory (JEP 351) Support for -XX:SoftMaxHeapSIze Support for the Linux/AArch64 platform Reduced Time-To-Safepoint

JDK 12 (Released March 2019)

Support for concurrent class unloading Further pause time reductions

JDK 11 (Released September 2018)

Initial version of ZGC Does not support class unloading (using -XX:+ClassUnloading has no effect)

二、ZGC相關(guān)VM Options

General GC Options ZGC Options ZGC Dianostic Options (-XX:+UnlockDianosticVMOptions)

-XX:MinHeapSize, -Xms

-XX:InitialHeapSize, -Xms

-XX:MaxHeapSize, -Xmx

-XX:SoftMaxHeapSize

-XX:SoftRefLRUPolicyMSPerMB

-XX:ZAllocationSpikeTolerance

-XX:ZCollectionInterval

-XX:ZFragmentationLimit

-XX:ZMarkStackSpaceLimit

-XX:ZPath

-XX:ZUncommit

-XX:ZUncommitDelay

-XX:ZProactive

-XX:ZStatisticsForceTrace

-XX:ZStatisticsInterval

-XX:ZVerifyForwarding

-XX:ZVerifyMarking

-XX:ZVerifyObjects

-XX:ZVerifyRoots

-XX:ZVerifyViews

1、激活ZGC

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC

2、設(shè)置堆大小,堆

-Xmx

3、并發(fā)線程數(shù),并發(fā)線程數(shù)太多會(huì)導(dǎo)致占用太多cpu時(shí)間分片,太少會(huì)導(dǎo)致回收速度跟不上垃圾生產(chǎn)速度。如果系統(tǒng)追求的是低時(shí)延,盡量不要讓系統(tǒng)超負(fù)荷工作,cpu使用率盡量控制在70%以下

-XX:ConcGCThreads=

4、return unused memery to os

這里指的是設(shè)置了xms和xmx且xmx>xms的情況,zgc默認(rèn)會(huì)返回未使用的內(nèi)存給操作系統(tǒng),對(duì)于內(nèi)存水位是重要指標(biāo)的系統(tǒng),返回未使用內(nèi)存可以更好的觀察內(nèi)存使用情況。但如果要禁用這個(gè)功能,可以使用:-XX:-ZUncommit 。但無(wú)論使用哪種策略,jvm不會(huì)uncommit unsed memery導(dǎo)致堆大小小于xms。這也意味著如果配置xms=xmx,該特性會(huì)被隱式禁用

5、Enable Large Pages

啟用方式:-XX:+UseLargePages

Large Pages在Linux稱為Huge Pages,配置zgc使用Huge Pages可以獲得更好的性能(吞吐量、延遲、啟動(dòng)時(shí)間),并且基本沒(méi)有缺點(diǎn),除了配置稍微復(fù)雜一點(diǎn)。配置Huge Pages大小,需要注意JVM除了堆以外其他需要使用到的內(nèi)存也得算進(jìn)去,具體配置方法如下,就不翻譯了:

6、 Enable Transparent Huge Page(THP)

一般不建議在對(duì)延時(shí)敏感的系統(tǒng)下使用,THP一個(gè)使管理Huge Pages自動(dòng)化的抽象層。

7、Enable NUMA Support

zgc默認(rèn)開啟NUMA支持,意味著在分配堆內(nèi)存時(shí),會(huì)盡量使用NUMA-local的內(nèi)存(比跨die訪問(wèn)快3倍)。但當(dāng)jvm發(fā)現(xiàn)程序使用的只是cpu的一個(gè)子集(限定使用),則會(huì)自動(dòng)禁用該特性。一般不需要關(guān)注這個(gè)特性,如果需要指定,可以通過(guò)以下參數(shù)指定

-XX:+/-UseNUMA

8、Enable GC logging

總結(jié)

以上是生活随笔為你收集整理的JDK之ZGC介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。