对象的实例化过程

文章详细介绍了Java对象从加载、内存分配、初始化、设置对象头到对象访问定位的全过程,包括类加载机制、指针碰撞与空闲列表的内存分配策略、初始值设置、对象头的类型指针和运行时数据、TLAB(本地线程分配缓冲)的作用,以及对象在堆中的存储布局和句柄与直接指针两种访问方式的优缺点。
摘要由CSDN通过智能技术生成

在这里插入图片描述

1、类加载

当虚拟机遇到一个 new 指令的时候,会先去检测这个指令的参数是否能定位到这个类的符号引用,并检查这个类是否被加载、链接(验证、准备、解析)、初始化过(在 JVM 的方法区中检查)。如果没有,则执行类加载(类加载机制)

2、内存分配

在类加载通过之后,虚拟机将为新生对象分配内存,对象所需内存的大小在类加载完成后便可完全确定,相当于从 Java 堆中抽取一块内存出来;而根据内存的是否绝对规整,分为指针碰撞和空闲列表两种分配方式:

指针碰撞:假设 Java 堆中的内存是绝对规整的,分为空闲和非空闲两种,中间用一个指针当做划分界限的指示器;当一个新对象需要分配对象时,相当于把指针向空闲区域移动一段与对象大小相等的距离。

空闲列表:假设 Java 堆的内存不是绝对规整的,空闲和非空闲是相互交错的,那就需要一个 OopMap 列表,用来记录哪些内存块是可以用的,在对象分配内存时,划分一块大小相等的区域给对象,并更新这个列表

从上面的解释看,用哪种分配方式,是通过 Java 堆的内存块是否绝对规整决定的。

堆内存是否规整,主要是看 GC 回收了内存之后是否包含压缩或者整理功能.如果有,那么内存就比较规整.否则如果没有,创建对象就需要采用空闲列表的方式.

比如:serial,ParNew 等带有整理的收集器,可以使用指针碰撞.

CMS 使用简单清除的算法,可以使用空闲列表.

但对象的创建是频繁的,在并发的情况,多线程不一定是安全的,即存在 A 对象在分配内存,指针还未来得及修改,B 对象也同时使用了原来的指针来分配对象。所以又衍生了两种解决办法,CAS+失败重试 和 TLAB 两种方式

CAS+失败重试:虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性 (关于 CAS 锁,是乐观锁的一种实现,解释起来也比较麻烦。

TLAB:本地线程分配缓冲,把内存分配的动作按照线程分配划分在不同的空间中进行,即每个线程在 Java 堆中预先分配一小块内存,哪个线程需要需要分配,先在 TLAB 中分配,用完了并重新分配新的 TLAB 时,才需要同步锁定。

3、初始值为零

在内存分配完成之后,虚拟机需要将分配到的内存空间初始化为零值 (除对象头外),这一步操作也保证了对象的实例字段在 java 代码中可以不赋初始值就可以使用,因为程序能访问这些字段的数据类型所对应的零值。

4、设置对象头

初始值设置之后,怎么知道对象是哪个类的实例,如何才能找到类的元数据信息、哈希码、GC 分代年龄等信息呢?这就需要对对象头进行一些必要的设置,才能定位到。

5、入栈、执行 init 指令

从虚拟机来看,对象已经分配产生完成了,且入栈了;但 Java 程序来看,这才刚开始,所以,new 之后,则执行 init 方法,进行初始化。

6、Java 对象的内存分布(即实例化后的对象在堆中的分布)

对象在内存中的存储布局可分为 3 部分:

在这里插入图片描述

对象头

其中对象头又可以细分为两部分:

1、存储对象自身运行时数据:如哈希码、GC 分代年龄、锁状态标志、线程持有的、偏向线程 ID 等信息

2、类型指针:即对象指向它的类元数据的指针,虚拟机通过这个来确定这个对象是哪个类的实例(比如是指向栈中的类声明)

实例数据

是对象真正存储的有效信息,比如程序中定义的各种类型的字段内容,无论父类和子类都会记录下来;在分配时,相同宽度的字段会被分配到一起,这也是父类定义的变量会出现在子类之前的原因。

对齐填充

没啥实际意义,就是为了保证对象是 8 个字节的整数倍,没对齐时,用来补全而已。

7、对象的访问定位

使用对象时,通过栈上的 reference 数据来操作堆上的具体对象。

建立对象是为了使用对象,Java 程序需要通过栈上的 reference 数据来操作堆上的具体对象;但这些访问方式取决于虚拟机实现而定,目前主流有句柄和直接指针两种:

句柄:从 Java 堆中划分出一块内存用来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄包含了对象实例数据与类型数据各自的具体地址信息,如下图(图片来自 Java 虚拟机第三版)

在这里插入图片描述

直接指针:在直接指针中,reference 储存的就是对象地址,所以,需要考虑的是如何防止访问类型数据的相关信息(图片来自 Java 虚拟机第三版)

在这里插入图片描述

优点介绍:
句柄:使用句柄好处是,reference 中存放的是文档的句柄地址,对象被移动时,只改变句柄的实例数据指针,而 reference 本身不需要修改

直接指针:使用直接指针的最大好处就是速度更快,节省了指针定位的开销;

HotSpot 使用第二种方式进行对象访问的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值