JVM 创建对象过程

Java 中对象的创建方式一般有两种:1)new xxx() 通过new关键字创建实例对象;2)通过反射创建对象。不管哪一种创建方式,JVM 底层的执行过程是一样的。
在这里插入图片描述

示例程序:

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();
    }}

1、检查类是否加载

当需要创建一个类的实例对象时,需要先判断该类是否被成功加载过,若没有加载,要先进行类的加载,如果加载过,会在堆区有一个类的 Class 对象,方法区会有类的相关元数据信息

类的加载过程:
(1)加载阶段:Person.class 文件经过加载后,会把类的相关信息加载到 JVM 内存中,解析出类的描述信息会保存到 Metaspace,同时在堆中生成一个代表这个类的 java.lang.Class 对象,作为这个类的各种数据的访问入口

在这里插入图片描述

(2)连接阶段:会对静态变量的值进行默认赋值,此时 Person 类的 staicVariabl 赋值为0

在这里插入图片描述
在这里插入图片描述

(3)初始化阶段:首先会对类的静态变量 staicVariabl 进行显示赋值(此时staicVariabl =1);然后收集类的静态代码块内容,生成一个类的 <clinit>() 方法并执行(此时staicVariabl =2)

在这里插入图片描述

2、创建对象

当我们执行上面代码中 main 方法中的的 Person person=new Person() 时,我们的对象就开始创建了。

(1)main 线程申请栈空间

首先 main 线程会在栈中申请一个自己的栈空间,然后调用 main 方法后会生成一个 main 方法的栈帧,然后执行 new Person() 。

(2)分配内存并实例变量初始化默认值

类加载成功后,JVM 会根据 Person 类元信息确定对象的大小了,然后 JVM 会在堆内存划分一块对象大小的内存空间出来分配给新生对象,同时对 Person 对象成员变量赋默认值,这样的话对象就可以在没有赋值情况下使用了,只不过访问对象的成员变量都是零值。
在这里插入图片描述
JVM 分配内存一般有两种方式:1.指针碰撞法 2.空闲列表

(1) 指针碰撞:前提堆中的内存空间比较规整,维护一个内存指针,然后通过移动指针的方式给对象分配内存空间。`JVM 默认使用指针碰撞分配内存。
在这里插入图片描述
(2)空闲列表:堆中的内存并不规整,比较凌乱,JVM 通过维护一个空闲列表的方式,为新生对象分配内存,这个空间列表中保存了所有空闲的内存,当对象需要分配内存时,从空闲列表中找到一块足够对象大小的内存,分给新生对象。
在这里插入图片描述
这种方式可能会产生一些碎片空间。因为并不是每次都凑巧能找到正好对象大小的空间,很多时候都是大于对象带下的,造成部分空间浪费。

(3)初始化对象

执行对象内部生成的 init() 方法(包括显示初始化成员变量值和 {} 初始化代码块两部分),最后执行对象构造方法(init() 方法执行完后 objVariabl=88,构造方法执行完后objVariabl=99)
在这里插入图片描述

(4)引用对象

对象实例化完毕后,再把栈中的 Person 对象引用地址指向 Person 对象在堆内存中的地址。
在这里插入图片描述

3、对象在内存的布局

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

(1)对象头

在这里插入图片描述

对象头里主要:锁状态标志、持有锁的线程ID、,GC分代年龄、对象HashCode,类元信息地址、数组长度。

1) 锁状态标志:对象的加锁状态分为无锁、偏向锁、轻量级锁、重量级锁几种标记。
2)持有锁的线程: 持有当前对象锁定的线程ID。
3)GC分代年龄: 对象每经过一次GC还存活下来了,GC年龄就加1。
4)类元信息地址: 可通过对象找到类元信息,用于定位对象类型。
5) 数组长度: 当对象是数组类型的时候会记录数组的长度。

(2)实例数据

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

(3)对齐填充

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

Person 对象最终创建完成后内存中数据情况大概如下图:
在这里插入图片描述
示例:
在这里插入图片描述
内存图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值