对象实例默认分配在堆内存中,引用变量存储其地址;new指令触发类加载、堆内存分配(指针碰撞或空闲列表)、零值初始化、设置对象头、执行构造函数;引用本质是压缩或非压缩指针,指向堆中对象起始地址。

Java 中对象的内存分配和指针指向,核心在于 JVM 的内存模型与对象创建机制。对象实例一定分配在堆内存中(除非开启逃逸分析后被栈上分配优化),而引用变量本身存储的是指向堆中对象的地址(逻辑上的“指针”),并非传统 C/C++ 中的直接内存地址。
对象在堆中的分配过程
当执行 new 指令时(如 new Person()),JVM 会按以下步骤分配空间:
- 检查类是否已加载、解析、初始化;未完成则先触发类加载过程
- 在堆中为对象分配内存:根据对象类型确定所需大小,从堆的空闲区域划分一块连续内存(使用指针碰撞或空闲列表策略)
- 将分配到的内存空间初始化为零值(不包括对象头),保证字段即使没显式赋值也不会是随机值
- 设置对象头信息:包括哈希码、GC 分代年龄、锁状态标志、类型指针(指向 Class 对象)等
- 执行 <init> 方法(即构造函数),完成用户定义的初始化逻辑
引用变量与“指针”的本质
Java 中没有暴露给程序员的原始指针,但每个引用变量(如 Person p = new Person(); 中的 p)在 JVM 内部都保存着一个“引用值”,它代表堆中对象的起始地址(HotSpot 中实际是 OOP Handle 或直接指针,取决于是否启用压缩指针)。
- 启用 -XX:+UseCompressedOops(默认开启,且堆小于 32GB 时生效):引用用 32 位压缩指针,通过基地址 + 偏移量定位对象
- 关闭压缩指针或堆过大时:引用为 64 位真实地址(在 64 位 JVM 上)
- 引用变量本身可能存放在栈帧的局部变量表中(方法内声明),也可能作为其他对象的字段存在(此时字段值就是该引用)
对象头与类型指针的关键作用
堆中每个对象开头都有对象头,其中“类型指针”(Klass Pointer)至关重要:
立即学习“Java免费学习笔记(深入)”;
- 它指向该对象所属类的元数据(即 java.lang.Class 在方法区/元空间中的实例)
- JVM 靠它识别对象类型,支撑多态、类型检查、反射等能力
- 注意:数组对象的对象头中还额外包含数组长度信息,普通对象则没有
常见误区澄清
容易混淆的几个点需要明确:
- String str = "hello" 创建的字符串字面量,在 JDK 7+ 后存于堆中的字符串常量池(不是方法区),str 引用仍指向堆内地址
- 基本类型变量(如 int i = 5)不涉及堆,值直接存于栈或寄存器;只有包装类(如 Integer i = new Integer(5))才在堆中创建对象
- 局部引用变量消失(方法结束)不代表对象立即回收——只要还有可达引用链,GC 就不会清理该对象


















