JVM对象内存分配方式取决于垃圾收集器是否执行内存压缩整理:若GC执行Compact使堆规整,则采用指针碰撞(如新生代Eden区);否则使用空闲列表(如老年代CMS),二者本质是速度与灵活性的权衡。

JVM对象内存分配方式不是由开发者手动指定,而是由垃圾收集器(GC)的行为自动决定的。核心依据只有一个:Java堆内存是否规整。而堆是否规整,又取决于所用GC是否在回收后执行内存压缩整理(Compact)。
指针碰撞适用于带压缩能力的收集器
当GC执行完回收并把存活对象往一端挪动、清出连续大片空闲空间时,堆就变成“规整”状态——已用内存和空闲内存泾渭分明,中间只需一个指针做分界。此时JVM采用指针碰撞:
- 分配动作极快:仅需原子地将指针向空闲侧移动对象大小的距离(Bump操作)
- 典型场景是新生代Eden区:Serial、ParNew、Parallel Scavenge等收集器默认启用Compact,Minor GC后Eden重置为连续空闲区
- 依赖底层支持:需配合TLAB(Thread Local Allocation Buffer)缓解并发竞争,否则多线程下必须用CAS保证指针更新的原子性
空闲列表用于不整理内存的收集器
如果GC只标记并清除对象,不移动存活对象(如CMS早期版本、ZGC的某些阶段),堆中就会留下大量零散空洞,内存不再连续。这时指针碰撞失效,JVM改用空闲列表:
- 维护一个链表结构,每个节点记录一块可用内存的起始地址与大小
- 分配时需遍历查找合适块(常用首次适应或最佳适应策略),再拆分并更新链表
- 老年代是主要使用场景:对象存活久、碎片多,CMS、G1(部分模式)、ZGC在非压缩路径下都依赖Free List机制
选择逻辑链条很清晰
不用记具体收集器名称,抓住因果链即可判断:
- GC是否执行Compact → 决定堆是否规整
- 堆是否规整 → 决定能否用指针碰撞
- 不能用指针碰撞 → 自动 fallback 到空闲列表
- 同一JVM中不同代可能混用:比如新生代用ParNew(指针碰撞),老年代用CMS(空闲列表)
本质上,这是JVM对硬件效率与内存灵活性做的权衡:指针碰撞赢在速度,空闲列表赢在适应性。










