java对象分配采用指针碰撞或空闲列表:前者适用于规整堆内存,速度快但要求连续空间;后者适应碎片化堆,通过链表管理空闲块,速度较慢但灵活性高。

Java堆中对象分配不是“随便找个空地放进去”,而是由JVM根据内存现状自动选择两种底层策略之一:指针碰撞或空闲列表。选哪个不取决于开发者意愿,而取决于堆当前是否规整——规整就用指针碰撞,碎片多就用空闲列表。
指针碰撞:快,但只认“整齐”的堆
它依赖一个前提:已用内存和空闲内存泾渭分明,中间只隔着一个分界指针。比如Minor GC后刚整理过的Eden区,就是典型规整状态。
- 分配动作极简:指针直接向后移动对象所需字节数,新内存区域立即可用
- 时间复杂度是O(1),速度接近硬件级,几乎无额外开销
- 但一旦内存不连续、或对象太大超出剩余空间,就会失效
- Serial、Parallel Scavenge、G1年轻代默认走这条路;不过多线程下必须配合TLAB或CAS同步,否则会冲突
空闲列表:慢,但能“见缝插针”
当堆里布满小块空闲内存(比如CMS老年代标记-清除后),指针碰撞就无法工作,JVM转而维护一张链表,把所有可用内存块登记下来。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 每次分配都要遍历链表,按策略(如首次适应)找一块够用的空闲块
- 找到后可能还要切割:用掉一部分,把剩余小块重新加回列表
- 对象回收时,对应内存块又被加回列表,形成闭环
- CMS、ZGC老年代、G1的Humongous区都重度依赖这种机制
JVM怎么决定用哪种?看堆的“身体状况”
不是配置决定,而是实时判断:
- 新生代Eden区多数时候规整 → 倾向指针碰撞(尤其开了TLAB后,每个线程有自己小指针)
- 老年代长期积累对象,碎片难避免 → 几乎只能靠空闲列表拼凑空间
- 大对象(如超大数组)可能绕过Eden直入老年代,或进G1的Humongous区 → 跳过指针碰撞逻辑,直接查空闲列表
- 哪怕用了Parallel GC,如果TLAB太小或被禁用(-XX:-UseTLAB),线程争抢全局指针失败,也会退化到空闲列表
线程安全靠配套机制兜底,不是策略自带
指针碰撞本身不线程安全——多个线程同时推同一个指针会错乱。所以JVM不会裸用:
- TLAB是主流解法:每个线程预分一小块私有Eden空间,各自维护本地指针,完全无锁
- 没TLAB或TLAB不够时,才同步申请Eden区公共空间,此时用CAS原子操作更新全局指针
- 空闲列表操作也需要同步,比如链表增删节点时加锁或使用并发数据结构
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










