TLAB默认开启,无需手动配置;其作用是为每个线程分配私有Eden子区域以实现无锁对象分配,仅小对象优先分配于此,大对象或逃逸分析后栈上分配则绕过TLAB。

TLAB 默认就开着,别手动关
HotSpot JVM 从 JDK 6 起默认启用 -XX:+UseTLAB,你几乎不需要做任何事它就在工作。手动加这个参数纯属冗余;而显式关闭(-XX:-UseTLAB)只会让所有对象分配退回到 Eden 区全局指针 + CAS 同步路径,高并发下 slow_allocations 暴涨、分配延迟翻倍——这在压测中很容易复现,但生产环境绝不能试。
看 GC 日志确认 TLAB 是否真在干活
光看参数不等于它在生效。启动时加上 -XX:+PrintGCDetails -XX:+PrintTLAB(JDK 8/11),或 -Xlog:gc+alloc=debug(JDK 10+),观察日志里是否出现类似:
TLAB: gc thread: 0x00007f8b4c00a000 desired_size: 1024KB slow_allocations: 2 refill_waste: 8KB
重点关注三个字段:
-
slow_allocations高(比如 >5% 的总分配次数)→ TLAB 太小或对象偏大,频繁 fallback 到共享 Eden 分配 -
refill_waste持续 >50KB 或占 Eden 比例超 1% → TLAB 过大,造成内存浪费和 Eden 碎片化 - 完全没看到 TLAB 行输出 → 可能被其他参数抑制(如用了
-XX:-UseTLAB)或 GC 算法特殊(ZGC 在某些版本默认禁用 TLAB 日志)
调大小不如信 JVM 自适应
JVM 默认开启 -XX:+ResizeTLAB,会根据线程的对象分配速率动态调整每个线程的 TLAB 大小。人为固定 -XX:TLABSize 容易出问题:
- 设太小 → 频繁 refill,
slow_allocations上升,反而增加同步开销 - 设太大 → 单个线程占满 Eden 可用连续空间,其他线程申请 TLAB 失败率升高
- 只在明确观测到
refill_waste长期 >10% 且 Eden 碎片率高时,才考虑微调-XX:TLABWasteTargetPercent
大对象和逃逸分析绕过 TLAB 是正常行为
TLAB 只负责小对象的无锁分配,以下情况天然不走 TLAB:
- 对象大小超过当前 TLAB 剩余空间 → 触发 refill;refill 失败则直接在 Eden 共享区分配(仍需 CAS)
- 显式指定大对象阈值:
-XX:PretenureSizeThreshold以上对象直接进老年代,根本不碰 Eden 和 TLAB - 逃逸分析成功 → 对象栈上分配,连堆都不进,TLAB 完全不参与
看到 OutOfMemoryError: Java heap space 不要第一反应去调 TLAB——95% 以上是对象泄漏、缓存未清理或集合无限增长,和 TLAB 无关。
TLAB 的关键不在“怎么开”,而在“怎么确认它没被绕过又没白浪费”。真正容易被忽略的是:TLAB refill 本身仍需 Eden 区加锁,只是频次极低;一旦线程分配模式突变(比如某批请求批量创建中等尺寸对象),refill 就可能成为新的热点,这时得结合 -Xlog:gc+alloc=debug 看具体哪个线程、哪类对象在触发慢分配。










