对象实例化的方式
创建对象的方式
1.new:直接new,或者单例模式中调用类内部的静态方法(类内自己new),或者xxxbuilder,xxxFactory(本质都是new)
2.Class的newInstance() 或者 Constructor 的newInstance(可带参数)前者已过时,被后者替代
3.clone(),不调用任何构造器,需要接口Cloneable的clone()方法
4.反序列化:从文件、网络中获取对象的二进制流。
5.第三方库
创建过程(字节码角度)
先加载到方法区,在堆内创建对象,栈里存放对象的引用
具体创建过程
1.判断这个类是否被加载
虚拟机遇到new指令,检查元空间常量池是否有这个类的符号引用,并检查他是否被加载、链接、初始化。如果没有加载,双亲委派机制,如果没有找到.class,那么报ClassNotFoundException
2.分配内存
先计算对象的大小,然后判断内存是否规整
如果规整:
指针碰撞(把用过的内存放一边,没用过的放一边,中间放一个指针,分配内存时,指针向空闲那边移动对象大小的距离)
如果不规整:使用空闲列表
内存空间是碎片的,虚拟机维护一个列表,记录哪些内存是可用的,再找出足够大的空间给对象分配,并更新表,这种方式叫“空闲列表”
3.处理并发安全问题
采用CAS失败重试、区域加锁保证更新的原子性
每个线程分配TLAB
4.初始化分配到的空间
所有属性设置默认值,保证对象实例字段在不赋值的时候可以直接使用
5.设置对象头
6.执行init方法初始化
属性显式初始化,代码块中初始化,构造器中初始化
对象的结构
对象头,实例数据,对齐填充
对象头
- 普通对象对象头(64bit):
- 运行时元数据(Mark Word 32bit):哈希值,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳
- 类型指针(Klass Word 32bit):指向元空间中的类元数据InstanceKlass,确定类的类型。
- 数组对象对象头(96bit):
- 比普通对象多个数组长度(array length 32bit)
实例数据
包括程序代码中定义的各种类型的字段(包括父类继承的)
规则:相同宽度的字段总是被分配在一起
父类定义的变量会出现在子类之前
如果COmpactFields参数为true,子类的窄变量可能插入到父类变量的空隙。
对齐填充
占位符,不用看。
对象访问定位
jvm如何通过栈帧的引用访问对象实例的呢