tlab分配失败走慢路径性能下降5~10倍,源于全局锁竞争、eden碎片升高及gc频次上升三重开销;需通过-xlog:gc+alloc=debug或-xx:+printtlab观察refill和waste指标,并结合jstat与async-profiler量化分析。

TLAB 分配失败后走慢路径,性能会掉 5~10 倍,不是理论值,是实测吞吐下降和 GC 频次上升的叠加结果。
怎么看 TLAB 失败是否正在发生
光看 OutOfMemoryError: Java heap space 没用——绝大多数时候和 TLAB 无关。真要确认失败,得开日志:
- JDK 10+:启动加
-Xlog:gc+alloc=debug,搜TLAB allocation failed或refill requested - 旧版 JDK:用
-XX:+PrintTLAB -XX:+PrintGCDetails,观察每行里waste字段是否持续 >10%,actual_size是否远小于desired_size - 别只看一次 GC 日志,要跑 5 分钟以上真实流量,否则压测噪声会掩盖线程分配节奏
慢路径到底慢在哪几个环节
不是“多了一次内存拷贝”那么简单,而是三重开销叠加:
- 必须抢全局
Allocation Lock,线程阻塞 + CAS 自旋 + 上下文切换,尤其在 QPS 过万时,Refill requested刷屏就是信号 - 对象直接落在 Eden 起始位置,破坏指针碰撞(Bump Pointer)的连续性,Eden 区碎片率升高,提前触发
ParNewGC - GC 日志里出现大量
humongous allocation,说明大对象(>TLAB 剩余空间一半)频繁绕过 TLAB,这类分配本身就要 CAS 抢共享内存,且易引发担保失败
怎么量化这个损耗而不是凭感觉
不能只看平均延迟,得拆开看链路:
- 用
jstat -gc <pid></pid>对比EC(Eden 容量)和EU(Eden 已用),如果 EU 波动剧烈、GC 频次突增,再结合 PrintTLAB 里 refill 次数/秒 > 线程数 × 2,基本可断定 TLAB 成了瓶颈 - 用
Async-Profiler采样CollectedHeap::common_mem_allocate_init的热点占比,若超过 15%,说明慢路径已侵入关键路径 - 禁用 TLAB 做对照实验:
-XX:-UseTLAB,实测吞吐下降 30%~50%,这就是 TLAB 本该扛住的那部分竞争压力
真正难调的不是参数本身,而是线程分配行为的波动性——比如某个定时任务线程突然批量 new 几百个中等大小对象,瞬间耗尽 TLAB,但日志里只体现为单次 allocation failed,容易被忽略。这种毛刺型退化,必须结合线程级分配采样(如 jdk.ObjectAllocationInNewTLAB 事件)才能定位。










