1. 创建对象的六个步骤
a. 判断对象对应的类是否加载,链接, 初始化
虚拟机遇到一个new指令, 首先去检查这个指令的参数是否能在metaspace的常量池中定位到一个类的符号引用, 并且检查这个符号引用代表的类是否已经被加载, 连接 ,初始化,(即判断类元信息是否存在), 如果没有, 那么再双亲委派模型下, 使用当前类加载器以ClassLoader + 包名 + 类名为key进行查找对应的class文件, 如果没有找到文件, 则抛出ClassNotFoundException异常, 如果找到, 则进行类加载, 并生成对应的Class类对象
b. 为对象分配内存
- 如果内存规则 使用指针碰撞
- 如果内存不规则 虚拟机需要维护一个列表(空闲列表), 记录哪些内存块是可用的, 再分配的时候从列表中找到一块足够大的空间划分给对象实例, 并更新列表上的内容, 这种分配方式被称为空闲列表
c. 处理并发问题
- 采用CAS失败重试, 区域加锁保证更新的原子性
- 每个线程先分配一块TLAB
d. 初始化分配到的空间 所有属性设置默认值, 保证对象实例字段在不赋值时可以直接使用
e. 设置对象的对象头
f. 执行init方法进行初始化
2. 对象的内存模型
a . 对象头的组成部分
- 运行时元数据 ( Mard Word)
用于存储对象自身的运行时数据, 如哈希码, GC分代年龄, 锁状态标志, 线程持有的锁, 偏向线程ID, 偏向时间戳
- 类型指针 ( Klass Word)
类型指针指向对象所属类的指针, JVM通过这个确定这个对象属于哪个类
注意 :
- klass pointer由64位8个字节组成,但我们使用的64位 JVM会默认使用选项 +UseCompressedOops 开启指针压缩,将指针压缩至32位。即上面截图中的klass pointer为4个字节32位。
- JVM启动时会进行一系列的复杂活动,比如装载配置,系统类初始化等等。在这个过程中会使用大量synchronized关键字对对象加锁,且这些锁大多数都不是偏向锁。为了减少初始化时间,JVM默认延时加载偏向锁。这个延时的时间大概为4左右,具体时间因机器而异。当然我们也可以设置JVM参数 -XX:BiasedLockingStartupDelay=0 来取消延时加载偏向锁。
https://blog.csdn.net/zhaocuit/article/details/100208879
https://www.cnblogs.com/lusaisai/p/12748869.html
https://blog.csdn.net/zhoufanyang_china/article/details/54601311
b . 对象实例数据
c . 对象填充
JVM要求对象占用的空间必须是8的倍数, 方便内存分配(以字节为最小单位分配) , 因此这部分就是用于填充不够的空间凑数用的
3. 对象的访问方式
- 句柄访问
- 直接指针