[TOC]
对象的创建
new指令
虚拟机遇到new指令,检查指令的参数是否存在于常量池中,若存在,检测是否被加载。
类加载
详见类加载章节。
分配内存
类加载完成后,分配对象内存。(从java堆中分配一块内存,其大小在类加载完成后就已确定。)
分配内存的方式有两种,取决于内存的规整性:
- 指针碰撞(内存规整)
- 空闲列表(内存散乱)
内存分配的同步问题解决方案:
- CAS + 失败重试
- TLAN(本地线程分配缓冲):即每个线程中预留一小块内存。(开启方式:-XX:+/-UseTLAB)
内存空间初始化
虚拟机将分配到的内存空间进行初始化为“0”。
设置对象头
对象头的信息包含 哪个类的实例,如何找到类的元数据信息,对象的哈希码,对象的GC分代年龄等。还有对象的偏向锁等等。对象头的介绍放于下一小节。
对象初始化init(java层面)
这一步就是将对象按照程序员的意愿将对象进行相应初始化,(init方法应该指的是类定义中字段处的初始化值和构造器的调用,自己的猜测)。
对象的内存布局
内存布局分为3部分:
- 对象头
- 实例数据
- 对齐填充
对象头
运行时数据
对象头的第一部分存储的是对象的运行时数据:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
类型指针
对象头的第二部分是类型指针,即对象指向他的类元数据的指针,用来确认这个对象是哪个类的实例。
同时通过元数据信息能够确认java对象的大小。
但如果是数组对象的话,对象头还会有一块用于记录数组长度的数据。
实例数据
这部分存储的是程序代码中定义的各种类型的字段内容,包括父类和子类中定义的。
对齐填充
这部分仅仅起占位符的作用。用来确保对象头正好占用8字节的整数倍。
对象的访问定位
句柄方式定位
从图中可以看出,句柄包含对象实例指针和对象类型指针。
该方式的优点是Reference的值在对象移动时不需要改变(垃圾回收整理)。
直接指针定位
优点速度快,减少一次指针定位的时间开销。
Sun hotSpot使用的就是这个方式。