创建对象的几种方式
1.new对象,相信大家最常用的就是这个
存在两种变形
1.类的静态方法,例如单例模式
2.builder以及factory方法
2.Class的newInstance():反射的方式,但是只能使用空参构造器,权限也必须是pubilc
3.Constructor的newInstance(XXX):依然是反射的方式,可以调用有参的构造器,权限没有限制
4.使用反序列化:从文件或者网络中获取对象的二进制流
5.clone:类需要实现Cloneable接口,实现clone()方法
6.第三方库
创建对象的步骤
1. 判断对象是否加载,链接,初始化
2. 为对象分配内存【指针碰撞/空闲列表】
3. 处理并发安全问题【采用cas/为每个线程分配自己的TLAB】
4. 初始化分配到的空间【变量默认值初始化,例如int默认值0,boolean默认值false等】
5. 设置对象头【类元信息/hashcode和对象的GC信息以及锁信息,具体设置方式取决于jvm】
6. 调用init方法进行初始化
对象的内存布局
对象头
1.运行时元数据
1.哈希值
2.GC分代年龄
3.所状态标示
4.线程持有的锁
5.偏向线程ID
6.偏向时间戳
2.类型指针:指向类元信息确定该对象所属类型
如果记录的是数据还需要记录一下数组的长度
实例数据
对象真正存储的有效信息,包括程序代码中定义的各种类型的字段(包括从父类继承下来的和本身拥有的)
相同宽度的对象总是被分配在一起,父类中定义的变量会出现在子类之前,如果CompactFields参数设置为true则子类的窄变量可以穿插到父类变量的空隙
对齐填充
不是必须的也没有特定含义,仅仅起到占位符的作用。
对象的访问定位方式
句柄池
句柄池是栈帧中的引用指向一个句柄池,句柄池中包括指向堆空间实例的指针以及一个指向方法区类型的指针。
优点是当GC后对象的位置发生了变化的话栈中的引用不需要发生变化因为句柄池的地址是没有发生变化的
缺点就是多了一次指针查找需要的性能损耗
直接指针
与句柄池对应,直接指针是栈中的引用指向堆中的实例,实例中存在一个指向方法区中对象类型的指针。
优点是节省了一次指针定位的时间开销
缺点是当GC后对象的地址发生了变化的话栈中引用所存储的地址也会发生变化。