java基础-- new 一个对象的过程

java new 一个对象的流程大体如下
java基础-- new 一个对象的过程
java基础-- new 一个对象的过程

public class Person {

//静态变量
public static int staicVariabl=1;
//成员变量
public int objVariabl;

//静态初始代码块
static {
staicVariabl=2;
}

//对象初始化代码块
{
objVariabl=88;
}

//构造方法
public Person() {
objVariabl=99;
}

public static void main(String[] args) {
Person person=new Person();
}

}

一、编译
经过编译后Person.java 会生成一个Person.class文件。

java基础-- new 一个对象的过程

二、装载
Person.class经过加载后,会把类的相关信息加载到JVM内存中,解析出类的描述信息会保存到Metaspace。
java基础-- new 一个对象的过程

三、连接
连接阶段会对静态变量的值进行默认赋值,此时Person类的staicVariabl 赋值为0;

java基础-- new 一个对象的过程

四、初始化
1、首先会对Person类的静态变量staicVariabl 进行真正的赋值的操作(此时staicVariabl =1)。

2、然后收集类的静态代码块内容,生成一个类的() 方法并执行(此时staicVariabl =2)。

java基础-- new 一个对象的过程

五、对象创建
当我们new一个对象的时候JVM首先会去找到对应的类元信息,如果找不到意味着类信息还没有被加载,所以在对象创建的时候也可能会触发类的加载操作。当类元信息被加载之后,我们就可以通过类元信息来确定对象信息和需要申请的内存大小。

对象创建的流程
当我们执行上面代码中main方法中的的 Person person=new Person() 时,我们的对象就开始创建了,执行流程大致分为三步:

1、构建对象:首先main线程会在栈中申请一个自己的栈空间,然后调用main方法后会生成一个main方法的栈帧。然后执行new Person() ,这里会根据Person类元信息先确定对象的大小,向JVM堆中申请一块内存区域并构建对象,同时对Person对象成员变量信息并赋默认值。

java基础-- new 一个对象的过程

2、初始化对象:然后执行对象内部生成的init方法,初始化成员变量值,同时执行搜集到的{}代码块逻辑,最后执行对象构造方法(init 方法执行完后objVariabl=88,构造方法执行完后objVariabl=99)。

java基础-- new 一个对象的过程

3、引用对象:对象实例化完毕后,再把栈中的Person对象引用地址指向Person对象在堆内存中的地址。
java基础-- new 一个对象的过程

六、对象在内存的布局
对象创建完成后在内存中保存了保存的信息包括对象头、实例数据及对齐填充三类信息。

对象头:
对象头里主要包括几类信息,分别是锁状态标志、持有锁的线程ID、,GC分代年龄、对象HashCode,类元信息地址、数组长度,这里并没有对对象头里的每个信息都列出而是进行大致的分类,下面是对其中几类信息进行说明。

锁状态标志:对象的加锁状态分为无锁、偏向锁、轻量级锁、重量级锁几种标记。

持有锁的线程: 持有当前对象锁定的线程ID。

GC分代年龄: 对象每经过一次GC还存活下来了,GC年龄就加1。

类元信息地址: 可通过对象找到类元信息,用于定位对象类型。

数组长度: 当对象是数组类型的时候会记录数组的长度。

实例数据
对象实例数据才是对象的自身真正的数据,主要包括自身的成员变量信息,同时还包括实现的接口、父类的成员变量信息。

对齐填充
根据JVM规范对象申请的内存地址必须是8的倍数,换句话说对象在申请内存大小时候8字节的倍数,如果对象自身的信息大小没有达到申请的内存大小,那么这部分是对剩余部分进行填充。

Person对象最终创建完成后内存中数据情况大概如下图:

java基础-- new 一个对象的过程

补充:

一、 java是使用双亲委派来进行类的加载的

双亲委托模型的工作过程是:
如果一个类加载器(ClassLoader)收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。

使用双亲委托机制的好处是:能够有效确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。

二、通过实例引用调用实例方法的时候,先从方法区中对象的实际类型信息找,找不到的话再去父类类型信息中找。

如果继承的层次比较深,要调用的方法位于比较上层的父类,则调用的效率是比较低的,因为每次调用都要经过很多次查找。这时候大多系统会采用一种称为虚方法表的方法来优化调用的效率

所谓虚方法表,就是在类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及其地址,包括父类的方法,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。当通过对象动态绑定方法的时候,只需要查找这个表就可以了,而不需要挨个查找每个父类。