说起对象的创建过程,就必须要先从类的初始化开始,只有先将类加载进内存进行初始化后,才能创建对象。
对象的创建过程
类的初始化过程:
1、加载 Loading
将类.class文件加载进内存(存放于内存中的class静态常量池中)
2、连接 Linking
a、验证
验证.class文件的格式
b、准备
给类的**静态变量**赋默认值
c、解析
将符号引用转换为直接引用
3、初始化
先执行静态代码块,再给静态的成员变量赋真实的值
好了,到这里,类的初始化就完成了。那么接下来的步骤就开始为对象的创建进行准备了。
4、申请对象内存
普通对象:
1、对象头markword : 占用8个字节
markword信息主要包括
锁信息,垃圾回收信息,分代信息和hashcode等
2、ClassPonter指针(指向.class对象的指针) :
根据启动参数
-XX:+UseCompressedClassPointers 是否开启
如果开启了,对象指针占用4个字节
如果未开启,对象指针占用8个字节
3、实例数据
根据启动参数是否设置
-XX:+UseCompressedOops(ordinary object pointers)
引用类型占用的空间:
如果开启,则为4字节
如果未开启,则为8字节
4、Padding对齐数据,总体要为8的倍数
数组对象:
1、对象头markword
2、classPointers类对象指针
3、数组长度4个字节(此处不同,其他都相同)
4、数组的实例数据
5、padding,对齐8的倍数
对象内存布局图:
特殊说明:在JDK1.6以前,Class对象存储在方法区中,从1.7开始,Class对象就存放在了堆中了。
5、成员变量赋默认值
6、调用构造方法
在调用构造方法前,成员变量会依次赋初始值,然后再执行构造方法。
到这里,对象就创建好了
但是并不是所有的对象开始分配都会在堆中分配的。对象的分配流程是什么样的呢?
对象在内存中的分配
对象如果足够小会尝试栈上分配。栈上分配失败后尝试TLAB。
TLAB同样也是在eden区为对象分配一块空间,但是分配的空间供该线程独享。是为了解决多线程中的对象空间分配冲突。
对象足够大时会直接分配进老年代,否则会在eden区分配一块空间。在eden区的对象经过minorGC后,次数达到了垃圾回收器的分代年龄后会进入老年代。
空间分配担保:在MinorGC之前,虚拟机会检查老年代的最大可用空间是否大于新生代的所有对象总和,如果大于,则此次MinorGC是安全的。否则虚拟机会查看参数HandlePromotionFailure是否允许空间分配担保失败,如果允许,则虚拟机会判断老年代的最大可用空间是否大于历次从eden区到老年代对象的平均值,如果大于,则进行一次MinorGC,否则进行FullGC