TLAB溢出本身不是错误,但每秒超2–3次refill requested即为高频分配卡点信号;需结合refills与waste判断:refills突增且waste长期偏低(500–2000字节)表明对象大小集中、TLAB利用率高,而waste忽高忽低则提示碎片化分配;同时出现大量slow path或humongous allocation说明对象绕过TLAB直走eden cas,拖累吞吐;应联动jstat -gc观察ec/eu与线程used累加值匹配度,eu持续>85%且refills高即表明eden快速打满;refills最高线程id需回溯对应业务逻辑,并关注parnewgc次数是否同步激增;tlabsize不可硬设,应先启用-xx:+resizetlab压测收敛refills至个位数,再用-xx:tlabwastetargetpercent=1控浪费率,大促场景可锁死大小并禁用resize。

TLAB 溢出本身不是错误,但当对象大小超过当前线程 TLAB 剩余空间,又不满足 refill 条件时,就会退回到 Eden 区走同步分配路径——这正是性能损耗的源头。
为什么绕过 TLAB 会明显拖慢吞吐
TLAB 的核心价值是消除分配锁竞争。一旦退到 Eden 区:
- 每次分配都要触发 CAS 更新共享的 Eden 分配指针(bump-pointer),高并发下极易发生失败重试
- 多个线程争抢同一块内存区域,CPU 缓存行频繁失效(false sharing),加剧总线压力
- 日志中出现大量 slow path allocation 或 humongous allocation,就是这种降级行为的直接证据
什么情况下对象会“被迫”走 Eden 同步路径
关键看两个阈值是否被突破:
- 对象尺寸 ≥ TLAB 剩余空间,且剩余空间 > _refill_waste_limit:JVM 认为“浪费得起”,就直接在 Eden 分配,不 refill
- 对象尺寸 > TLAB 最小允许值(MinTLABSize,默认 2KB)但 < PretenureSizeThreshold:仍属小对象范畴,本该走 TLAB,却因空间碎片或 waste 限制被拒
- 显式禁用或线程刚启动尚未分配 TLAB:极少见,多见于调试场景
如何识别和量化这种损耗
不能只看单条日志,要聚合分析:
- 开启 -XX:+PrintTLAB,统计 60 秒内各线程的 refills 总数 和 slow path 次数;若后者占 refill 相关事件的 15% 以上,说明 Eden 竞争已成瓶颈
- 对比 jstat -gc pid 1000 5 中的 EU(Eden 已用)与所有线程 used 累加值:若 EU 持续 >85% 且远高于累加值,说明大量分配发生在共享 Eden,而非 TLAB
- 观察 GC 日志中 ParNewGC 次数是否随 refills 高峰同步激增:Eden 快速打满 → 更高频 Minor GC → 分配延迟雪上加霜
优化方向不是“加大 TLAB”,而是匹配分配节奏
硬设大 TLAB(如 -XX:TLABSize=1024k)反而可能造成内存浪费或 GC 压力不均:
- 先启用 -XX:+ResizeTLAB 压测,让 JVM 自动收敛 refills 到个位数/秒
- 再用 -XX:TLABWasteTargetPercent=1 控制平均浪费率,平衡 refill 频次与空间利用率
- 大促等确定性场景可锁死大小(如 512KB)并关闭 resize,避免运行时抖动,但需确保对象大小分布稳定

















