ZGC 原理是什么,它为什么能做到低延时?

ZGC的成绩是,无论你开了多大的堆内存(1288G? 2T?),硬是能保证低于10毫秒的JVM停顿。

SPECjbb 2015基准测试,在128G的大堆下,最大停顿时间才 1.68ms (不是平均,不是90%,99%,是Max ! ),远低于最初的目标-那保守的10ms,也远胜前代的G1。

大家的第一反应都是这么颠覆性的东西怎么来的,G1 通过每次只回收部分Region而不是全堆,改善了大堆下的停顿时间,但在普通大小的堆里表现并没惊喜。现在怎么突然就翻天了,一点心理准备都没有啊。

如果文章太长不想看下去,你只要记住R大下面这句话就够了:

与标记对象的传统算法相比,ZGC在指针上做标记,在访问指针时加入Load Barrier(读屏障),比如当对象正被GC移动,指针上的颜色就会不对,这个屏障就会先把指针更新为有效地址再返回,也就是,永远只有单个对象读取时有概率被减速,而不存在为了保持应用与GC一致而粗暴整体的Stop The World。

其实Azul JDK的皇牌 C4 垃圾收集 ,早就同样以最高十毫秒停顿成为江湖传说。 曾在Azul的R大, 看着JDK11 ZGC的算法和结果倍感熟悉,与ZGC的领队Per Liden大大聊完之后,确认了ZGC跟Azul Pauseless GC,是,等,价,的

[图片上传失败...(image-68a273-1648567376486)]

下面让我们来继续聊聊ZGC的八大特征。

一、所有阶段几乎都是并发执行的

这里的并发(Concurrent),说的是应用线程与GC线程齐头并进,互不添堵。

说几乎,就是还有三个非常短暂的STW的阶段,所以ZGC并不是Zero Pause GC啦。

R大:“比如开始的Pause Mark Start阶段,要做根集合(root set)扫描,包括全局变量啊、线程栈啊啥的里面的对象指针,但不包括GC堆里的对象指针,所以这个暂停就不会随着GC堆的大小而变化(不过会根据线程的多少啊、线程栈的大小之类的而变化)” -- 因此ZGC可以拍胸脯,无论堆多大停顿都小于10ms。

二、并发执行的保证机制,就是Colored Pointer 和 Load Barrier

原理前面R大一句话已经说完了。Colored Pointer 从64位的指针中,借了几位出来表示Finalizable、Remapped、Marked1、Marked0。 所以它不支持32位指针也不支持压缩指针, 且堆的上限是4TB。

image.png

有Load barrier在,就会在不同阶段,根据指针颜色看看要不要做些特别的事情(Slow Path)。注意下图里只有第一种语句需要读屏障,后面三种都不需要,比如值是原始类型的时候。

image.png

R大还提到了ZGC的Load Value Barrier,与Red Hat的Shenandoah收集器的不同,后者选择了70年代的比较基础的Brooks Pointer ,而前者在也是很老的Baker barrier上加入了self healing的特性,比如下面的代码:

Object a = obj.x;

Object b = obj.x;

两行代码都插入了读屏障,但ZGC在第一个读屏障之后,不但a的值是新的,self healing下obj.x的值自身也会修正,第二个读屏障时就直接进入FastPath,没有消耗了; 而Shenandoah 则不会修正obj.x的值,第二个读屏障又要SlowPath一次。

三、像G1一样划分Region,但更加灵活

ZGC将堆划分为Region作为清理,移动,以及并行GC线程工作分配的单位。

不过G1一开始就把堆划分成固定大小的Region,而ZGC 可以有2MB,32MB,N× 2MB 三种Size Groups,动态地创建和销毁Region,动态地决定Region的大小。

256k以下的对象分配在Small Page, 4M以下对象在Medium Page,以上在Large Page。

所以ZGC能更好的处理大对象的分配。

image.png

四、和G1一样会做Compacting-压缩

CMS是Mark-Sweep标记过期对象后原地回收,这样就会造成内存碎片,越来越难以找到连续的空间,直到发生Full GC才进行压缩整理。

ZGC是Mark-Compact ,会将活着的对象都移动到另一个Region,整个回收掉原来的Region。

而G1 是 incremental copying collector,一样会做压缩。

下面粗略了几十倍地过一波回收流程,小阶段都被略过了哈:

1. Pause Mark Start -初始停顿标记

停顿JVM地标记Root对象,1,2,4三个被标为live。

2. Concurrent Mark -并发标记

并发地递归标记其他对象,5和8也被标记为live。

3. Relocate - 移动对象

对比发现3、6、7是过期对象,也就是中间的两个灰色region需要被压缩清理,所以陆续将4、5、8 对象移动到最右边的新Region。移动过程中,有个forward table纪录这种转向。

R大这里又赞扬了一下C4/ZGC的Quick Release特性:活的对象都移走之后,这个region可以立即释放掉,并且用来当作下一个要扫描的region的to region。所以理论上要收集整个堆,只需要有一个空region就OK了。

而RedHat的Shenandoah 因为它的forward pointer的设计,则需要有1/2个Heap是空的。

4. Remap - 修正指针

最后将指针都妥帖地更新指向新地址。这里R大还提到一个亮点: “上一个阶段的Remap,和下一个阶段的Mark是混搭在一起完成的,这样非常高效,省却了重复遍历对象图的开销。”

五、没有G1占内存的Remember Set,没有Write Barrier的开销

G1 保证“每次GC停顿时间不会过长”的方式,是“每次只清理一部分而不是全部的Region”的增量式清理。

那独立清理某个Region时 , 就需要有RememberSet来记录Region之间的对象引用关系, 这样就能依赖它来辅助计算对象的存活性而不用扫描全堆, RS通常占了整个Heap的20%或更高。

这里还需要使用Write Barrier(写屏障)技术,G1在平时写引用时,GC移动对象时,都要同步去更新RememberSe,跟踪跨代跨Region间的引用,特别的重。而CMS里只有新老生代间的CardTable,要轻很多。

ZGC几乎没有停顿,所以划分Region并不是为了增量回收,每次都会对所有Region进行回收,所以也就不需要这个占内存的RememberSet了,又因为它暂时连分代都还没实现,所以完全没有Write Barrier。

六、支持Numa架构

现在多CPU插槽的服务器都是Numa架构了,比如两颗CPU插槽(24核),64G内存的服务器,那其中一颗CPU上的12个核,访问从属于它的32G本地内存,要比访问另外32G远端内存要快得多。

JDK的 Parallel Scavenger 算法支持Numa架构,在SPEC JBB 2005 基准测试里获得40%的提升。

原理嘛,就是申请堆内存时,对每个Numa Node的内存都申请一些,当一条线程分配对象时,根据当前是哪个CPU在运行的,就在靠近这个CPU的内存中分配,这条线程继续往下走,通常会重新访问这个对象,而且如果线程还没被切换出去,就还是这位CPU同志在访问,所以就快了。

但可惜CMS,G1不支持Numa,现在ZGC 又重新做了简单支持,哈哈哈。

R大补充,G1也打算支持了Numa了: http://openjdk.java.net/jeps/157

七、并行

在ZGC 官网上有介绍,前面基准测试中的32核服务器,128G堆的场景下,它的配置是:

20条ParallelGCThreads,在那三个极短的STW阶段并行的干活 - mark roots, weak root processing(StringTable, JNI Weak Handles,etc)和 relocate roots ;

4条ConcGCThreads,在其他阶段与应用并发地干活 - Mark,Process Reference,Relocate。 仅仅四条,高风亮节地尽量不与应用争抢CPU 。

ConcCGCThreads开始时各自忙着自己平均分配下来的Region,如果有线程先忙完了,会尝试“偷”其他线程还没做的Region来干活,非常勤奋。

八、单代

没分代,应该是ZGC唯一的弱点了。所以R大说ZGC的水平,处于AZul早期的PauselessGC 与 分代的C4算法之间 - C4在代码里就叫GPGC,Generational Pauseless GC。

分代原本是因为most object die young的假设,而让新生代和老生代使用不同的GC算法。但C4已经是全程并发算法了,为什么还要分代呢?

R大说:

“因为分代的C4能承受的对象分配速度(Allocation Rate), 大概是原始PGC的10倍。

如果对整个堆做一个完整并发收集周期,持续的时间可能很长比如几分钟,而此期间新创建的对象,大致上只能当作活对象来处理,即使它们在这周期里其实早就死掉可以被收集了。如果有分代算法,新生对象都在一个专门的区域创建,专门针对这个区域的收集能更频繁更快,意外留活的对象更也少。

而Per大大因为分代实现起来麻烦,就先实现出比较简单可用的单代版本。所以ZGC如果遇上非常高的对象分配速率,目前唯一有效的“调优”方式就是增大整个GC堆的大小来让ZGC有更大的喘息空间。”

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容