一、对象的实例化
1.1、创建对象常用的方式
-1、最常用的方式
new Object()
- 2、Class的newInstance
- 3、Constructor的newInstance(xxx)
- 4、使用clone()
- 5、使用反序列化
- 6、第三方库Objenesis
1.2、创建对象的过程
- 1、判断对象对应的类是否加载、链接、初始化
- 2、为对象分配内存
- 如果内存规整:指针碰撞
- 如果内存不规整:虚拟机需要维护一个列表、空闲列表分配
- 3、处理并发安全问题
- 采用CAS配上失败重试保证更新的原子性
- 每个线程预先分配一块TLAB
- 4、初始化分配到空间:所有属性设置默认值,保证对象实例字段在不赋值时可以直接使用
- 5、设置对象的对象头
- 6、执行 init 方法进行初始化
二、对象的内存布局
2.1、对象头(Header)
- 运行时元数据(Mark Word)
- 1、哈希值
- 2、GC分代年龄
- 3、锁状态标志
- 4、线程持有的锁
- 5、偏向线程ID
- 6、偏向时间戳
- 类型指针:指向类元数据InstanceKlass,确定该对象所属的类型
2.2、实例数据(Instance Data)
- 它是对象真正存储的有效信息,包括程序代码中定义的各种类型的字段(包括从父类继承下来的和本身拥有的字段)
- 相同宽度的字段总是被分配在一起
- 父类中定义的变量会出现在子类之前
- 如果CompactFields参数为true(默认为true):子类的窄变量可能插入到父类变量的空隙
2.3、对齐填充(Padding)
- 不是必须的,也没特别含义,仅仅起到占位符的作用
三、对象的访问定位
- 创建对象的目的是为了使用
- JVM是如何通过栈帧中的对象引用访问到其内部的对象实例的
- 对象访问方式主要有两种
-
1、句柄访问
-
2、直接指针(HotSpot采用)
-
1、句柄访问
四、实例分析
4.1、code
package com.lkty.method;
public class Customer {
int id = 10086;
String name;
Account account;
{
name = "匿名客户";
}
public Customer() {
this.account = new Account();
}
}
class Account {
}
4.2、测试代码
package com.lkty.method;
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
}
}