TLAB分配无需原子指令,仅通过线程本地指针偏移实现无锁对象分配;CAS仅在TLAB耗尽时用于Eden区共享分配;PLAB则专用于GC晋升,体现JVM分层降级的无锁设计哲学。

TLAB 分配本身不生成汇编指令——它根本不需要原子操作,也就没有 cmpxchg 类指令参与。所谓“看穿高并发无锁美学”,关键不在对比汇编,而在理解 JVM 如何用内存布局规避竞争:TLAB 是空间换时间,CAS 是时间换安全。
TLAB 的本质是“免同步的指针偏移”
当线程在自己的 TLAB 中分配对象时,JVM 只需执行类似这样的本地操作:
- 读取当前 TLAB 的 top 指针(一个普通 load)
- 计算新对象起始地址:top + size
- 检查是否越界(一次 compare,如 cmp rax, rdx)
- 若未越界,直接把 top 更新为新地址(一次 store,如 mov [tlab_top], rax)
全程无锁、无内存屏障、无 cmpxchg。它不涉及跨线程可见性问题,因为这块内存只归当前线程逻辑独占。你用 hsdis 或 JITWatch 查看热点分配代码,看到的是干净利落的寄存器算术和内存写入,没有任何原子指令痕迹。
CAS 分配出现在 TLAB 耗尽后的兜底路径
只有当对象太大放不进 TLAB,或首次分配尚未获得 TLAB 时,JVM 才退回到 Eden 区的共享分配路径。这时才真正调用 CAS:
- JVM 调用 Unsafe.compareAndSwapInt/Long(底层映射为 x86 的 cmpxchg)
- 该指令会触发缓存锁(cache lock),锁定当前缓存行,确保其他核不能同时修改 Eden 分配指针
- 失败则重试循环(自旋),可能伴随 pause 指令降低功耗
你在汇编里看到的 cmpxchg、jne loop、pause,就是传统并发控制的“挣扎感”——它高效但有成本,而 TLAB 的目标正是让这段汇编极少被执行。
PLAB 是另一条平行优化线,不参与分配主路径
PLAB(Promotion Local Allocation Buffer)专用于 GC 过程中的对象晋升(从 Survivor 到 Old),和对象新建无关。它同样采用线程本地缓冲思想,但作用域在 GC 线程内。它的存在进一步说明:JVM 把“避免竞争”的设计哲学贯穿到了内存生命周期的每个阶段,不只是分配,也包括回收。
真正的“无锁美学”在于分层降级的设计直觉
不是所有地方都硬上 CAS,而是按概率分级:
- 95%+ 小对象 → TLAB:纯 bump-the-pointer,零同步开销
- 少数大对象或 TLAB refill → CAS + 自旋:可控的乐观重试
- 极端情况(如 TLAB 频繁 refill 或 GC 压力大)→ 触发全局同步(如 Eden 锁或 GC 暂停)
这种结构不靠单点技术炫技,而靠对真实负载分布的理解:用局部性(locality)消解并发,用退化路径兜住边界。你翻汇编看不到“美学”,但看懂分配慢路径的触发频率,就摸到了 JVM 并发设计的脉搏。

















