对象的创建
- 类加载检查
- 检查这个指令的参数是否能在常量池中定位到一个类的引用
- 检查这个符号引用代表的类是否已被加载、解析和初始化过, 如果没有,那必须先执行相应的类加载过程
- 确定对象所需内存的大小
- 为新生对象分配内存
- 初始化对象的字段, 大概例如 int类型的初始值0就是在这里初始化的吧
- 对对象进行必要的设置,设置对象头信息(对象的哈希码、GC分代年龄等)
- 执行<init>方法
对象的内存布局
- 对象头
- 第一部分:储存对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁等
- 第二部分:类型指针,虚拟机可以通过这个指针来确定这个对象是哪个类的实例。不是必须;如果对象是一个java数组,对象头还必须记录数组长度,因为从数组的元数据中无法确定数组的大小,对象可以;然后是实例数据,无论是父类继承的,还是子类定义的,相同宽度的字段总是分配到一起
- 第三部分:对齐填充,不是必然存在的。对象的大小必须是8字节的倍数,当对象大小不足以对齐时,就需要通过对齐填充来对齐
对象的访问定位
- 第一种,通过句柄访问,在java堆中划分出来一块内存作为句柄池,句柄池记录了对象实例数据、类型数据的指针,在java栈本地变量表中记录了句柄池的地址。优点:当句柄池中的指针有变化时,java栈中的地址可以不变。
- 第二种,通过直接地址访问,优点是比句柄访问方式速度更快,节省了定位句柄池。