RednaxelaFX、你假笨关于TLAB的一些分析总结

简书 占小狼
转载请注明原创出处,谢谢!

本文由臧秀涛撰稿,经过R大润色,由占小狼倾情分享,这些分析总结道出了TLAB的来龙去脉,不得不说R大语言基本功真是大写的服字。


在JVM研究群里,占小狼同学发来一篇文章:JVM源码分析之线程局部缓存TLAB

大家对相关概念还有有些疑问,RednaxelaFX、你假笨等朋友就此做了很多分享。

以下内容主要根据大家的问题和RednaxelaFX、你假笨的分享整理。

TLAB的目的是在为新对象分配内存空间时,让每个Java应用线程能在使用自己专属的分配指针来分配空间,均摊对GC堆(eden区)里共享的分配指针做更新而带来的同步开销。

TLAB只是让每个线程有私有的分配指针,但底下存对象的内存空间还是给所有线程访问的,只是其它线程无法在这个区域分配而已。当一个TLAB用满(分配指针top撞上分配极限end了),就新申请一个TLAB,而在老TLAB里的对象还留在原地什么都不用管——它们无法感知自己是否是曾经从TLAB分配出来的,而只关心自己是在eden里分配的。

TLAB简单来说本质上就是三个指针:start,top 和 end (实际实现中还有一些额外信息但这里暂不讨论)。
其中 start 和 end 是占位用的,标识出 eden 里被这个 TLAB 所管理的区域,卡住eden里的一块空间说其它线程别来这里分配了哈。而 top 就是里面的分配指针,一开始指向跟 start 同样的位置,然后逐渐分配,直到再要分配下一个对象就会撞上 end 的时候就会触发一次 TLAB refill。

要注意TLAB这个词其实有两层意思:一个是指存在管理Java线程的元数据对象 JavaThread 里的 ThreadLocalAllocBuffer对象,它持有上述三个指针,仅用于管理用而不存储对象自身;另一个是指在eden中分配出来的、被一个线程的ThreadLocalAllocBuffer所管理的一块空间,这才是实际存放对象的地方。本讨论不特地指出的时候会自由混用这两层意思,把它们当作一个整体来看待。

TLAB refill包括下述几个动作:

  • 将当前TLAB抛弃(retire)掉。这个过程中最重要的动作是将TLAB末尾尚未分配给Java对象的空间(浪费掉的空间)分配成一个假的“filler object”(目前是用int[]作为filler object)。这是为了保持GC堆可以线性parse(heap parseability)用的。
  • 从eden新分配一块裸的空间出来(这一步可能会失败)
  • 将新分配的空间范围记录到ThreadLocalAllocBuffer里
    TLAB refill不成功(eden没有足够空间来分配这个新TLAB)就会触发YGC。

注意“撞上”指的是在某次分配请求中,top + new_obj_size >= end 的情况,也就是说在被判定“撞上”的时候,top 常常离 end 还有一段距离,只是这之间的空间不足以满足新对象的分配请求 new_obj_size 的大小。这意味着在触发TLAB refill的时候,有可能会浪费掉位于该TLAB末尾的一部分空间:该TLAB已经占用了这块空间所以其它线程无法在这里分配Java对象,但该TLAB要refill的话它自己也不会在这块空间继续分配Java对象,从应用层面看这块空间就浪费了。

每次分配TLAB的大小不是固定的,而是每个线程根据该线程启动开始到现在的历史统计信息来自己单独调整的。如果一个线程上跑的代码的内存分配速率非常高,则该线程会选择使用更大的TLAB以达到均摊同步开销的效果,反之亦然;同时它还会统计浪费比例,并且将其放入计算新TLAB大小的考虑因素当中,把浪费比例控制在一定范围内。

GC很重要的一点是对heap parseability的依赖。GC做某些需要线性扫描堆里的对象的操作时,需要知道堆里哪些地方有对象而哪些地方是空洞。一种办法是使用外部数据结构,例如freelist或者allocation BitMap之类来记录哪里有空洞;另一种办法是把空洞部分也假装成有对象,这样GC在线性遍历时会看到一个“对象总是连续分配的”的假象,就可以以统一的方式来遍历:遍历到一个对象时,通过其对象头记录的信息找出该对象的大小,然后跳到该大小之后就可以找到下一个对象的对象头,依此类推。HotSpot选择的是后者的做法,假装成有对象的这种东西就叫做filler object(填充对象)。

实现代码上,

TLAB的慢速分配和重新申请空间的逻辑在这里:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/cf85f331361b/src/share/vm/gc_interface/collectedHeap.cpp#l264

申请好了空间并且zero完之后就会设置进TLAB里:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/cf85f331361b/src/share/vm/gc_interface/collectedHeap.cpp#l304

TLAB里的filler object是这样用的:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/cf85f331361b/src/share/vm/memory/threadLocalAllocBuffer.cpp#l110

CollectedHeap里的裸allocate动作是不关心分配的东西是什么类型的,只管在GC堆里看有没有地方可以分配,有的话bump分配指针并返回bump前的指针。TLAB从eden重新分配空间就是问CollectedHeap再allocate一块这样的裸的空间,然后把这块空间的首尾记录到自己的start和end里去。

另外建议把TLAB翻译为线程私有分配区,而不是线程局部分配缓存这样词对词的直译。毕竟TLAB并不是一个缓存,而且它的重点也不是局部,而是让那个分配指针成为线程私有的东西。

无论是加锁还是CAS,HotSpot的共享堆分配都是用碰撞指针(pointer bumping / bump-the-pointer)来做的。加锁跟bump不在一个层面上,不应该并列。锁或者CAS只是同步的机制,实际想要做的事情都一样是bump pointer。如果在不需要与其它线程竞争的条件下,bump pointer就不用同步保护。

例如在TLAB里,又例如在PLAB里,又例如在共享部分但在safepoint中没有竞争的情况下。

PLAB也是个非常有趣的东西,提到TLAB的话也可以顺带说下PLAB。HotSpot里的TLAB是只在eden里分配的,用于给新建的小对象用。(本来其实也有考虑让TLAB在任意位置分配,但后来没实现)。PLAB则是在old gen里分配的一种临时的结构。就是笨神说的promotion LAB。

在多GC线程并行做YGC的时候,大家都要为了晋升对象而在old gen里分配空间,于是old gen的分配指针就热起来了。大量的竞争会使得并行度降低,所以跟TLAB用同样的思路,old gen在处理YGC的晋升对象的分配也一样可以用(GC)线程私有的分配区。这就是PLAB。另外在CMS里old gen的剩余空间不是连续的,而是有很多空洞。这些剩余空间是通过freelist来管理的。

如果ParNew要把对象晋升到CMS管理的old gen,不优化的话就得在freelist上做分配。于是就可以通过类似PLAB的方式,每个GC线程先从freelist申请一块大空间,然后在这块大空间里线性分配(bump pointer)。这样就既降低了对分配指针/freelist的竞争,又可以降低freelist分配的频率而转为用线性分配。

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

推荐阅读更多精彩内容

  • TLAB整理 HotSpot VM在JAVA堆中对象创建,布局,访问全过程(仅限于普通java对象,不包括数组和C...
    andersonoy阅读 3,028评论 0 2
  • 作者:一字马胡 转载标志 【2017-11-12】 更新日志 日期更新内容备注 2017-11-12新建文章初版 ...
    beneke阅读 2,184评论 0 7
  • 一 、java虚拟机底层结构详解 我们知道,一个JVM实例的行为不光是它自己的事,还涉及到它的子系统、存储区域、...
    葡萄喃喃呓语阅读 1,472评论 0 4
  • JVM架构 当一个程序启动之前,它的class会被类装载器装入方法区(Permanent区),执行引擎读取方法区的...
    cocohaifang阅读 1,646评论 0 7
  • http://www.cnblogs.com/angeldevil/p/3801189.html值得一看 Clas...
    snail_knight阅读 1,409评论 1 0