tlab的核心作用是让绝大多数对象分配彻底绕过锁,线程仅操作自身私有缓冲区的本地指针,通过bump-the-pointer实现纳秒级无锁分配;其失效场景包括tlab空间不足、对象过大、eden区满或显式禁用。

TLAB 的核心作用不是“降低锁粒度”,而是让绝大多数对象分配彻底绕过锁——线程只操作自己那块内存里的本地指针,不碰任何共享状态。
TLAB 是怎么把锁“消失”的
没有 TLAB 时,100 个线程同时 new 一个对象,都要争抢 Eden 区唯一的 top 指针。这个操作必须靠 CAS 自旋或加锁完成,失败重试、缓存行失效、上下文切换全来了。
启用 TLAB 后,JVM 在 Eden 区内为每个线程划出一块互不重叠的连续空间。线程分配小对象时,只检查自己 TLAB 的剩余容量,然后执行一次指针加法(bump-the-pointer)并清零内存。整个过程在用户态完成,不涉及任何同步原语。
- 分配动作变成纯 CPU 寄存器操作:起始地址 + 当前偏移 → 新偏移,再写入对象头
- 不同线程的 TLAB 地址空间完全隔离,零共享、零冲突
- 90% 以上的对象分配因此降级为纳秒级操作
TLAB 不是万能的:哪些情况会重新触发锁
TLAB 只覆盖小对象的高频分配路径。以下情形会退出无锁模式,回到需要同步的共享区分配:
- 当前 TLAB 剩余空间不够放下该对象
- 对象大小超过 JVM 内部启发式上限(通常约 2–4KB,与 -XX:MaxTLABSize 相关)
- Eden 区已接近满,无法为线程成功 refill 新 TLAB
- 显式关闭:-XX:-UseTLAB(生产环境不建议)
此时分配会走共享 Eden 区,依赖 CAS 更新全局 top 指针,或在极端情况下触发 minor GC 来腾出空间。
怎么确认 TLAB 正在有效工作
不能只看是否开启,关键看运行时行为是否合理。启动 JVM 时加上:
-Xlog:gc+alloc=debug 或 -XX:+PrintGCDetails -XX:+PrintTLAB
关注 GC 日志中的字段:
- slow_allocations 占比高(如 >5%)→ TLAB 太小或对象偏大,频繁 fallback
- refill_waste 持续超过 50KB 或占 Eden 超 1% → TLAB 过大,造成碎片和浪费
- 完全没输出 TLAB 行 → 检查是否被其他参数抑制,或 GC 算法特殊(如 ZGC 某些版本默认不打日志)
JVM 默认启用 -XX:+ResizeTLAB,会根据线程分配速率动态调优大小。手动设 -XX:TLABSize 容易适得其反,仅在压测发现 waste 长期超标且 Eden 碎片率高时才微调 -XX:TLABWasteTargetPercent。
真正容易被忽略的关键点
TLAB refill 本身虽轻量,但仍是潜在同步点:它需要 CAS 更新 Eden 的全局分配指针。虽然竞争面极小(只在 TLAB 边界发生),但如果线程数量多、分配节奏高度一致,仍可能形成微小热点。
另外,逃逸分析成功后对象栈上分配、大对象直接进老年代、ThreadLocal 内部对象的创建——这些都不经过 TLAB,属于 JVM 更上层的优化路径,和 TLAB 是否启用无关。
看到 OutOfMemoryError: Java heap space,第一反应不该是调 TLAB,而应排查对象泄漏、缓存未清理或集合无限增长。










