JVM如何利用TLAB机制,通过线程私有分配区缓解堆内存分配中的锁竞争问题?
- 内容介绍
- 相关推荐
本文共计816个文字,预计阅读时间需要4分钟。
JVM通过为每个线程在Eden区划分一块私有内存(即TLAB),使对象分配从争抢同一块内存变为各有各的缓冲区,从而减少锁的开销。核心不在于加锁的粒度,而在于大量分配操作无需锁。
TLAB 是 Eden 区内的线程专属小块空间
TLAB 并非独立于堆外的新内存区域,而是 JVM 在新生代 Eden 区中为每个线程动态划出的一段连续内存。它属于堆的一部分,但逻辑上只对该线程可见和可写。线程启动时,JVM 自动为其分配一个初始大小的 TLAB;后续对象分配优先落在其中。
- 默认启用,无需手动开启(-XX:+UseTLAB 是默认行为)
- 大小受控:初始约占 Eden 的 1%,可通过 -XX:TLABWasteTargetPercent 调整目标占比
- 仅限小对象:超过 TLAB 剩余空间或超过预设阈值(如 -XX:TLABSize 或 JVM 内部启发式上限)的对象,直接绕过 TLAB
分配过程完全无锁:靠指针递增实现原子性
线程在自己的 TLAB 中分配对象,本质是移动一个本地指针(bump-the-pointer)。只要指针更新不越界,整个过程就是纯 CPU 寄存器操作,无需任何同步机制。
- 例如:TLAB 起始地址为 0x1000,当前指针在 0x1050,要分配一个 32 字节对象 → 检查 0x1050 + 32 ≤ TLAB 末地址 → 若成立,直接将指针设为 0x1070,分配完成
- 该操作由 JVM 在解释执行或 JIT 编译后生成高效机器码,单线程下天然线程安全
- 不同线程操作各自 TLAB,地址空间完全隔离,零共享、零冲突
TLAB 耗尽时的平滑回退机制
TLAB 不是永久绑定的。当线程用完当前 TLAB,JVM 会触发一次轻量级协调,而非全局加锁:
- 尝试向 Eden 申请一块新 TLAB(仍属 Eden 分配,但需 CAS 更新 Eden 的全局分配指针 —— 竞争面极小,因只在 TLAB 边界发生)
- 若新申请失败(如 Eden 几乎满),或对象过大,JVM 会直接在 Eden 共享区分配,此时才使用加锁(如 CAS 重试或偏向锁保护的分配指针)
- 旧 TLAB 剩余空间不浪费:JVM 可能将其标记为“已废弃”,等待下次 Young GC 一并回收
为什么这比细粒度锁更高效?
传统全局锁或分段锁仍需进入内核态、上下文切换、缓存行失效等开销。而 TLAB 将 90% 以上的对象分配降级为纯用户态指针运算,把锁竞争从“每次分配都可能发生”压缩到“每几千次分配才可能一次”。实测在高并发对象创建场景下,TLAB 可提升吞吐量 30%–50%。
本文共计816个文字,预计阅读时间需要4分钟。
JVM通过为每个线程在Eden区划分一块私有内存(即TLAB),使对象分配从争抢同一块内存变为各有各的缓冲区,从而减少锁的开销。核心不在于加锁的粒度,而在于大量分配操作无需锁。
TLAB 是 Eden 区内的线程专属小块空间
TLAB 并非独立于堆外的新内存区域,而是 JVM 在新生代 Eden 区中为每个线程动态划出的一段连续内存。它属于堆的一部分,但逻辑上只对该线程可见和可写。线程启动时,JVM 自动为其分配一个初始大小的 TLAB;后续对象分配优先落在其中。
- 默认启用,无需手动开启(-XX:+UseTLAB 是默认行为)
- 大小受控:初始约占 Eden 的 1%,可通过 -XX:TLABWasteTargetPercent 调整目标占比
- 仅限小对象:超过 TLAB 剩余空间或超过预设阈值(如 -XX:TLABSize 或 JVM 内部启发式上限)的对象,直接绕过 TLAB
分配过程完全无锁:靠指针递增实现原子性
线程在自己的 TLAB 中分配对象,本质是移动一个本地指针(bump-the-pointer)。只要指针更新不越界,整个过程就是纯 CPU 寄存器操作,无需任何同步机制。
- 例如:TLAB 起始地址为 0x1000,当前指针在 0x1050,要分配一个 32 字节对象 → 检查 0x1050 + 32 ≤ TLAB 末地址 → 若成立,直接将指针设为 0x1070,分配完成
- 该操作由 JVM 在解释执行或 JIT 编译后生成高效机器码,单线程下天然线程安全
- 不同线程操作各自 TLAB,地址空间完全隔离,零共享、零冲突
TLAB 耗尽时的平滑回退机制
TLAB 不是永久绑定的。当线程用完当前 TLAB,JVM 会触发一次轻量级协调,而非全局加锁:
- 尝试向 Eden 申请一块新 TLAB(仍属 Eden 分配,但需 CAS 更新 Eden 的全局分配指针 —— 竞争面极小,因只在 TLAB 边界发生)
- 若新申请失败(如 Eden 几乎满),或对象过大,JVM 会直接在 Eden 共享区分配,此时才使用加锁(如 CAS 重试或偏向锁保护的分配指针)
- 旧 TLAB 剩余空间不浪费:JVM 可能将其标记为“已废弃”,等待下次 Young GC 一并回收
为什么这比细粒度锁更高效?
传统全局锁或分段锁仍需进入内核态、上下文切换、缓存行失效等开销。而 TLAB 将 90% 以上的对象分配降级为纯用户态指针运算,把锁竞争从“每次分配都可能发生”压缩到“每几千次分配才可能一次”。实测在高并发对象创建场景下,TLAB 可提升吞吐量 30%–50%。

