对象实例化
对象创建过程
- 判断对象是否被加载, 加载类元信息
虚拟机遇到一条new指令, 首先去检查这个指令的参数能否在Metaspace的常量池中定位到一一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化。( 即判断类元信息是否存在).如果没有,那么在双亲委派模式下,使用当前类加载器以ClassLoader+包名+类名为Key进行查找对应的.class文件。如果没有找到文件,则抛出ClassNotF oundException异常,如果找到,则进行类加载,并生成对应的Class类对象 - 分配内存, 对象所需内存的大小在类加载完成后便可完全确定,如果实例成员变量是引用类型,则分配4字节
- 如果内存规整:指针碰撞
所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那 个指针向空闲空间方向挪动一段与对象大小相等的距离 - 如果内存不规整: 空闲列表
维护列表,记录哪些内存块是可用的,在分 配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录
- 如果内存规整:指针碰撞
- 处理并发问题
采用 CAS 失败重试, 区域加锁保证更新原子性
每个线程预留 TLAB - 初始化分配到的空间
属性默认初始化:零值 - 设置对象的对象头
对象是哪个类的实例、如何才能找到 类的元数据信息、对象的哈希码(实际上对象的哈希码会延后到真正调用O bject ::hashCode()方法时才计算)、对象的GC分代年龄等信息 - init 方法初始化
属性显式初始化:显示初始化, 代码块初始化, 构造器初始化
对象内存布局
- 对象头
- 运行时元数据
- hashCode
- GC 分代年龄
- 锁状态标志
- 线程持有的锁
- 偏向线程ID
- 偏向时间戳
- 类型指针
指向元数据 InstanceClass ,确定对象所属的类型
- 运行时元数据
- 实例数据
对象真正存储的有效信息,包括定义的各种类型字段 - 对齐填充
public class Customer{
int id = 1002;
String name;
Account acct;
{
name = "匿名";
}
public Customer(){
acct = new Account();
}
}
class Account{
}
JVM 如何通过对象引用找到对象实体?
-
句柄访问:
空间多,效率低;
对象移动方便 -
直接指针