tlab本质是用空间换时间,为每个线程在eden区内动态划分独立缓冲区,使90%以上对象分配退化为一次指针加法;其通过top/end指针实现无锁、无cas、零共享的纳秒级分配,仅在tlab耗尽、对象过大或显式禁用时回退至全局同步分配。

TLAB 的本质不是加锁优化,而是用空间换时间,把“抢同一块内存”变成“每人一块专属地”。它让 90% 以上的对象分配退化成一次指针加法,真正实现纳秒级分配。
TLAB 是 Eden 区里划出来的“线程包间”,不是新内存
它不脱离堆,也不额外申请内存,只是 JVM 在 Eden 区内为每个线程动态切出一段连续地址空间。线程启动时自动获得初始 TLAB(默认约 Eden 总大小的 1%),后续分配优先往自己这块里填。
- 每个 TLAB 有两个本地指针:top(当前已分配结束位置)和 end(缓冲区边界),仅该线程可见
- 分配小对象时只做一件事:检查
top + 对象大小 ≤ end;成立就直接把 top 加上对象大小,完成分配 - 这个过程纯 CPU 寄存器操作,无 CAS、无锁、无上下文切换,连内存屏障都不需要
为什么“不争”比“细粒度锁”更高效
没有 TLAB 时,100 个线程同时 new 对象,都要原子更新 Eden 唯一的全局分配指针——这会引发大量 CAS 自旋失败、缓存行失效、CPU 白耗。TLAB 把这个热点彻底打散:每个线程只动自己的 top/end,地址空间物理隔离,零共享、零冲突。
- 锁竞争从“每次分配都可能发生”压缩到“每几千次才触发一次 TLAB refill”
- refill 本身也极轻量:只需对 Eden 全局指针做一次 CAS 更新,而非每次分配都来一遍
- 旧 TLAB 剩余空间不会丢弃,会被标记为废弃,等下次 Young GC 统一回收
对象没走 TLAB 的三种典型情况
不是所有对象都能享受无锁快路径。理解这些 fallback 场景,才能解释监控中突增的分配延迟或 GC 频率。
- TLAB 空间不足:当前对象大小超过剩余可用空间,触发 refill;若 Eden 已满 refill 失败,则直接在共享 Eden 分配(需 CAS)
-
对象过大:超过 JVM 内部阈值(如默认约 256KB)或显式配置的
-XX:TLABSize,跳过 TLAB 直接走共享区 -
TLAB 被禁用:显式设置
-XX:-UseTLAB,所有分配回归全局锁竞争,高并发下易出现OutOfMemoryError: Java heap space
调优关键不在“开不开”,而在“配得准”
TLAB 默认开启,但大小不合适反而导致浪费或频繁 refill。核心是让 TLAB 容量匹配线程平均单次分配压力。
- 用
-XX:+PrintTLAB观察实际 refill 次数、浪费率(Waste)和平均大小 - 若 waste% 过高(如 >5%),说明 TLAB 太大,碎片多;可调小
-XX:TLABWasteTargetPercent - 若 refill 频率异常高(如 >1000 次/秒),说明 TLAB 太小,可适当增大
-XX:TLABSize或启用-XX:+ResizeTLAB让 JVM 动态调整 - 注意:TLAB 大小受 Eden 总容量制约,调大 TLAB 前建议同步增大
-Xmn或调整-XX:SurvivorRatio











