对象实例化内存布局与访问定位
对象实例化内存布局与访问定位
- 对象实例化内存布局与访问定位
- 对象实例化
- 创建对象的方式
- 创建对象步骤
- 对象内存分布
- 对象头
- 实例数据(Instance Data)
- 对齐填充(Padding)
- 访问定位
- 句柄访问(Handle)
- 直接指针访问(Direct Pointer)
对象实例化内存布局与访问定位
对象实例化
创建对象的方式
- 使用new关键字 (调用无参或者有参构造器创建)
- Class的newInstance方法(调用的是无参构造器,而且必须是Public修饰的构造器)
- Constructor类的newInstance方法
- clone方法,不调用构造器,对象需要实现Cloneable接口,默认为浅复制1
- 反序列化
- 三方库利用asm字节码技术2,动态生成对象2
创建对象步骤
-
判断对象对应的类是否加载 链接 初始化
当虚拟机需要创建对象的时候,首先判断这个创建对象的指令的参数,能不能在常量池中找到对应的类的符号引用,并检查符号引用所代表的类是不是已经被加载 解析 初始化;如果没有,使用双亲委派模式,以ClassLoader 和报名+类名为key查找对应的class文件;如果找不到对应的class文件,抛出异常;如果找到,就将class文件加载到内存,生成Class类对象。 -
分配内存
3.处理并发问题
创建对象是比较频繁的操作,如何保证创建新对象的线程安全性?有两种解决方式
- CAS
- TLAB
-
初始化内存空间
内存分配结束之后,申请到的空间的值不是固定的,所以拿到内存空间之后,还需要初始化内存空间,这一步将分配到的空间都初始化为0 -
设置对象头
JVM将对象所属类 对象的hashcode 对象的GC信息 锁信息这些数据 写入对象头 -
执行init方法初始化
这一步才是我们写Java代码感知到的初始化,也就是构造器。这一步开始初始化成员变量,执行实例代码块,调用构造器等等
对象内存分布
Java 对象在内存中通常由三部分组成(按顺序排列):
-
对象头(Object Header)
-
实例数据(Instance Data)
-
对齐填充(Padding)
对象头
- Mark Word
-
作用:存储对象自身的运行时数据
-
大小:32 位 JVM:4 字节;64 位 JVM:8 字节(开启指针压缩时为 4 字节)
-
unused:未使用的区域。
-
identity_hashcode:对象最原始的哈希值,就算重写hashcode()也不会改变。
-
age:对象年龄。
-
biased_lock:是否偏向锁。
-
lock:锁标记位。
-
ThreadID:持有锁资源的线程ID。
-
epoch:偏向锁时间戳。
-
ptr_to_lock_record:指向线程栈中lock_record的指针。
-
ptr_to_heavyweight_monitor:指向堆中monitor对象的指针。
-
Klass Pointer(类型指针)
-
数组长度(仅数组对象需要)
实例数据(Instance Data)
存储对象的所有字段值,排列顺序受以下规则影响:
-
基本类型优先:按 long/double → int/float → short/char → byte/boolean 的顺序
-
引用类型最后:所有引用类型(如 String, Object)放在末尾
-
父类字段优先:父类的字段出现在子类之前
-
紧凑排列策略:-XX:CompactFields=true(默认)允许子类窄字段插入父类空隙
对齐填充(Padding)
-
作用:确保对象总大小是 8 字节的倍数
-
原因:CPU 按块访问内存(通常 64 位系统按 8 字节对齐),对齐可提升访问效率
-
规则:对象头 + 实例数据 的总大小不是 8 的倍数时自动填充
访问定位
句柄访问(Handle)
工作流程
-
栈帧中的引用变量存储句柄池地址
-
通过句柄池地址找到句柄结构
-
句柄包含两个指针:
-
实例数据指针 → 指向堆中对象实例
-
类型数据指针 → 指向方法区类元数据
-
-
通过实例数据指针访问对象字段
优缺点
-
优点:对象移动安全:GC 移动对象时只需更新句柄中的实例指针,栈中引用不变;引用稳定:适合频繁移动对象的 GC 算法(如标记-整理)
-
缺点:性能损耗:每次访问需两次指针跳转(额外内存访问);内存占用:句柄池消耗额外内存(通常 4-8 字节/对象)
直接指针访问(Direct Pointer)
工作流程
-
栈帧中的引用变量直接存储堆内存地址
-
通过地址直接访问对象头
-
对象头中的 Klass Pointer 指向方法区类元数据
-
直接访问对象字段数据
优缺点
-
优点:访问速度快:减少一次内存寻址(性能提升 20-30%);内存紧凑:无句柄池开销,减少内存碎片
-
缺点:GC 复杂性:移动对象时需更新所有引用(需遍历栈、寄存器等);依赖优化:需要高效的 GC 算法(如复制算法+卡表)
引用类型的属性(如对象、数组),浅复制仅复制指向该子对象/数组的内存地址(引用),而不是在内存中创建一个全新的副本;对于基本数据类型,浅复制会直接复制值本身。 ↩︎
一个强大而底层的 Java 库,用于直接操作 Java 字节码(.class 文件) ↩︎ ↩︎