如 图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 所示。
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 期望大小 + 当前需要分配的空间大小