深入理解Java虚拟机(4)--对象的创建(虚拟机再遇到一条new指令时,会怎么做?)

我们在编写程序的时候经常使用new指令来创建一个对象,那么虚拟机再遇到一条new指令创建对象(仅限于简单对象,不包括数组和Class对象等)的时候,在虚拟机的内部到底是一个怎么样的过程。

首先介绍几个在创建对象的过程中使用到的几个生词。

1、Java分配内存的方式:

1.1、 指针碰撞
假设Java堆中内存是绝对完规整的,所有用过的内存都放在一边,空闲的内存都放在另一边,中间放着一个指针作为分界点的指示器,那所分配的内存就仅仅是把那个指针向空闲空间挪动一段与对象大小相等的距离。

1.2、 空闲列表:
若果Java堆中的内存不是规整的,已使用的内存和空闲的内存相互交错,虚拟机需要维护一个列表,记录上哪些内存块是可用的,在分配内存的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。

虚拟机选择哪种分配方式是由Java堆是否规整决定的,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能所决定

2、内存分配再多线程情况下存在的问题及解决方案:

2.1、存在的问题:
内存分配在并发情况下并不是线程安全的,可能出现正在给对象A分配内存,指针还没来的及修改,对象B又同时使用了原来的指针来分配内存的问题。

2.2、解决方案:
1.对内存空间的动作进行同步处理(实际上虚拟机采用CSA配上重试的方式保证更新操作的原子性);
2.把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一块小的内存,成为本地线程分配缓冲(Thread Local Allocation Buffer TLAB),哪个线程分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。

3、new 指令创建实例:

3.1、虚拟机在遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个符号引用,并检查这个符号引用代表的类是否已经被虚拟机加载过、解析、和初始化过。如果没有,就必须先执行类的加载过程。

3.2、在类加载检查通过后,接下来虚拟机回为新生的对象分配内存,对象所需内存的大小在加载完成后便可以确定,为对象分配空间的人物等同于将一块等同于对象所需内存大小的内存从Java堆中划分出来。这是开始使用上面提到的内存分配方式对新生对象进行内存的划分,在划分过程中对多线程情况下对内存划分不同步问题使用上述的解决方案来解决同步问题。

3.3、内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一操作保证了对象的实例字段在Java代码中可以不赋初始值就可以直接使用。

3.4、接下来虚拟机要对对象进行必要的设置,例如对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、GC的对象分代年龄等信息。这新信息储存在对象头中(Object Header)之中。

3.5、从虚拟机的角度来看,一个新的对象已经产生,但从Java的角度来看,对象的创建才刚刚开始,方法还没有执行,所有的字段还都为零,因此在执行完new指令之后会接着执行 方法,把对象按照程序员的意愿进行初始化,这样一个真正开用的对象才算创建完成。

总结

创建对象的流程图如下:

《深入理解Java虚拟机(4)--对象的创建(虚拟机再遇到一条new指令时,会怎么做?)》

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/mgl934973491/article/details/73607487
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞