前言
对象创建:查找类型,确认类加载,分配内存,初始化内存,设置对象,init🖊
对象构造:对象头(Markword,类型指针),实例数据
对象访问定位:从栈上的引用访问到对象,需要查找到堆中的对象,然后通过对象中的类型指针找到其类型
对象创建过程
这里讲的是普通的Java对象,不包括数组或Class对象
- 虚拟机遇到new指令时,检查指令参数是否能在常量池中定位到这个类的符号引用
- 检查这个符号引用代表的类是否已经被加载、解析和初始化过
- 若未加载,执行相应的类加载过程
- 为新生对象分配内存。需要内存大小在编译时就可完全确定。分配方式有几种,“空闲列表”、“指针碰撞”、“CAS+失败重试保证原子性”,“分配TLAB本地线程分配缓冲”
- 对内存空间初始化零值。这一步保证了对象的实例字段在不赋初始值就可以直接使用,能访问到队形数据类型所对应的零值
- 虚拟机对对象进行设置。例如是哪个类、对象哈希码、GC分代年龄、是否设置偏向锁等,对对象头Object Header设置。
- 执行
init
方法,按照程序员意愿初始化对象。这样才算是真正被完全生产出来对象(在这之前对象内都还是零值)
对象的内存布局
- 对象头:MarkWord+类型指针
- MarkWord:对象自身运行时的设置数据:哈希码HashCode,GC分代年龄,锁状态,持有锁,偏向线程ID等
- 类型指针:指向类元数据,虚拟机通过这个指针确定对象的类型
- 实例数据:包括对象定义的数据,而且还包括从父类继承的、子类定义的数据
- 对齐填充:补位的无意义填充
对象访问定位
Java程序是如何通过引用定位到对象实例数据和类型数据的?
虚拟机有两钟可实现的方式:
- 句柄方式:Java堆中划分出一个句柄池,其中每个句柄存放对象实例数据和类型数据各自的地址信息。特点是稳定,对象被移动只改变句柄不改变reference。
- 直接指针:Java堆的对象还存放一个类型的指针。速度更快,节省了1次指针定位的时间开销。(这正是HotSpot虚拟机的实现方式)