全网最硬核 JVM TLAB 分析(单篇版不包含额外加菜) (5)

图08 所示,如果指定了 TLABSize,就用这个大小作为初始期望大小。如果没有指定,则按照如下的公式进行计算:

堆给TLAB的空间总大小/(当前有效分配线程个数期望*重填次数配置)

堆给 TLAB 的空间总大小:堆上能有多少空间分配给 TLAB,不同的 GC 算法不一样,但是大多数 GC 算法的实现都是 Eden 区大小,例如:

传统的已经弃用的 Parallel Scanvage 中,就是 Eden 区大小。参考:parallelScavengeHeap.cpp

默认的G1 GC 中是 (YoungList 区域个数减去 Survivor 区域个数) * 区域大小,其实就是 Eden 区大小。参考:g1CollectedHeap.cpp

ZGC 中是 Page 剩余空间大小,Page 类似于 Eden 区,是大部分对象分配的区域。参考:zHeap.cpp

Shenandoah GC 中是 FreeSet 的大小,也是类似于 Eden 的概念。参考:shenandoahHeap.cpp

当前有效分配线程个数期望:这是一个全局 EMA,EMA 是什么之前已经说明了,是一种计算期望的方式。有效分配线程个数 EMA 的最小权重是 TLABAllocationWeight。有效分配线程个数 EMA 在有线程进行第一次有效对象分配的时候进行采集,在 TLAB 初始化的时候读取这个值计算 TLAB 期望大小。

TLAB 重填次数配置(refills time):根据 TLABWasteTargetPercent 计算的次数,公式为。TLABWasteTargetPercent 的意义其实是限制最大浪费空间限制,为何重填次数与之相关后面会详细分析。

8.1.2. TLAB 初始分配比例计算

图08 所示,接下来会计算TLAB 初始分配比例。

线程私有分配比例 EMA:与有效分配线程个数 EMA对应,有效分配线程个数 EMA是对于全局来说,每个线程应该占用多大的 TLAB 的描述,而分配比例 EMA 相当于对于当前线程应该占用的总 TLAB 空间的大小的一种动态控制。

初始化的时候,分配比例其实就是等于 1/当前有效分配线程个数。图08 的公式,代入之前的计算 TLAB 期望大小的公式,消参简化之后就是1/当前有效分配线程个数。这个值作为初始值,采集如线程私有的分配比例 EMA

8.1.3. 清零线程私有统计数据

这些采集数据会用于之后的当前线程的分配比例的计算与采集,从而影响之后的当前线程 TLAB 期望大小。

8.2. TLAB 分配

TLAB 分配流程如 图09 所示。

image

8.2.1. 从线程当前 TLAB 分配

如果启用了 TLAB(默认是启用的, 可以通过 -XX:-UseTLAB 关闭),则首先从线程当前 TLAB 分配内存,如果分配成功则返回,否则根据当前 TLAB 剩余空间与当前最大浪费空间限制大小进行不同的分配策略。在下一个流程,就会提到这个限制究竟是什么。

8.2.2. 重新申请 TLAB 分配

如果当前 TLAB 剩余空间大于当前最大浪费空间限制(根据 图08 的流程,我们知道这个初始值为 期望大小/TLABRefillWasteFraction),直接在堆上分配。否则,重新申请一个 TLAB 分配。
为什么需要最大浪费空间呢?

当重新分配一个 TLAB 的时候,原有的 TLAB 可能还有空间剩余。原有的 TLAB 被退回堆之前,需要填充好 dummy object。由于 TLAB 仅线程内知道哪些被分配了,在 GC 扫描发生时返回 Eden 区,如果不填充的话,外部并不知道哪一部分被使用哪一部分没有,需要做额外的检查,如果填充已经确认会被回收的对象,也就是 dummy object, GC 会直接标记之后跳过这块内存,增加扫描效率。反正这块内存已经属于 TLAB,其他线程在下次扫描结束前是无法使用的。这个 dummy object 就是 int 数组。为了一定能有填充 dummy object 的空间,一般 TLAB 大小都会预留一个 dummy object 的 header 的空间,也是一个 int[] 的 header,所以 TLAB 的大小不能超过int 数组的最大大小,否则无法用 dummy object 填满未使用的空间。

但是,填充 dummy 也造成了空间的浪费,这种浪费不能太多,所以通过最大浪费空间限制来限制这种浪费。

新的 TLAB 大小,取如下两个值中较小的那个:

当前堆剩余给 TLAB 可分配的空间,大部分 GC 的实现其实就是对应的 Eden 区剩余大小:

传统的已经弃用的 Parallel Scanvage 中,就是 Eden 区剩余大小。参考:parallelScavengeHeap.cpp

默认的G1 GC 中是当前 Region 中剩余大小,其实就是将 Eden 分区了。参考:g1CollectedHeap.cpp

ZGC 中是 Page 剩余空间大小,Page 类似于 Eden 区,是大部分对象分配的区域。参考:zHeap.cpp

Shenandoah GC 中是 FreeSet 的剩余大小,也是类似于 Eden 的概念。参考:shenandoahHeap.cpp

TLAB 期望大小 + 当前需要分配的空间大小

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppzys.html