Java虚拟机对象创建过程
1、对象的内存分配方法
-
类加载检查,首先检查类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
-
在类加载检查通过之后,需要为新生对象分配内存,分配内存有两种方法:
-
**指针碰撞**
-
**空闲列表**
-
-
对象创建应该是线程安全的,例如给对象分配完内存之后,需要修改栈区中指向堆内存堆的指针,这个过程未必是线程安全的。实现内存分配与指针修改的线程安全,有两种方法:
-
**CAS配上失败重试**
保证更新操作的原子性 -
**本地线程分配缓冲**
(Thread Local Allocation Buffer,TLAB),为每一个线程在Java堆中预先分配一小块内存,只有本地缓冲区用完之后,分配新缓存区才需要同步锁定
-
-
内存分配完成之后,虚拟机必须将分配到的内存空间(但不包括对象头)都初始化为零值,保证了在Java代码中可以不赋初始值就直接使用
-
**对象头**
进行设置,对象头包括:-
类的元数据信息
-
对象的哈希码
-
对象GC分代年龄
-
-
对对象进行初始化,包括执行构造函数、静态代码段执行
2、对象的内存把布局
-
对象在内存中的存储布局可以分为三个部分:
-
对象头
-
实例数据
-
对齐填充
-
-
对象头包括两部分信息
-
对象运行时数据,包括哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32个比特和64个比特,官方称它 为“Mark Word”。
-
对象类型指针,即对象指向它的类型元数据的指针,Java虚拟机通过这个指针 来确定该对象是哪个类的实例
-
-
实例数据,对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字段内容
-
对齐填充
- HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是 任何对象的大小都必须是8字节的整数倍,对不满足要求的部分进行填充。
3、对象的定位访问
-
对象的访问方式主要有两种:
-
句柄访问
-
直接访问
-
-
句柄访问
-
reference存放的是对象句柄地址,包括
**对象实例数据**
和**类型数据**
,实例数据位于堆内存中,指的是类中的各个字段。类型数据即对象的方法,位于方法区,由对象头中的对象类型指针指向 -
句柄访问的好处在于reference中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而 reference本身不需要被修改。
-
-
直接访问
- reference存储的是对象地址,避免了指针间接访问的开销
- reference存储的是对象地址,避免了指针间接访问的开销