【JVM】底层实现(二):创建一个对象的过程

本文深入剖析了Java中new关键字在JVM层面的实现过程,包括从constantpool获取 klassOop 对象、检查类是否可实例化、初始化类以及在堆内存中创建对象的细节。重点讨论了类初始化的步骤,包括线程安全、静态块执行以及对象创建的内存布局。同时,解释了instanceKlass对象的状态转换以及在多线程环境下的同步策略。
摘要由CSDN通过智能技术生成

定义两个简单的类AAA和BBB

img

通过javap -c AAA查看编译之后的字节码,具体如下:

img
new 指令的实现过程

Java 中的 new 关键字对应 jvm 中的 new 指令,定义在 InterpreterRuntime 类中

在这里插入图片描述

PS:其中 pool是 AAA 的 constant pool,此时 AAA 的 class 已经加载到虚拟机中,new 指令后面的#2表示BBB类全限定名的符号引用在constant pool的位置

1.pool->klass_at

方法pool->klass_at负责返回 BBB 对应的 klassOop 对象,实现如下:

img
如果常量池中指定位置(#2)的数据已经是个 oop 类型,说明 BBB 的 class 已经被加载并解析过,则直接通过(klassOop)entry.get_oop()返回 klassOop;

否则表示第一次使用 BBB,需要解析 BBB 的符号引用,并加载 BBB 的 class 类,生成对应的 instanceKlass 对象,并更新 constant pool 中对应位置的符号引用;

2.klass->check_valid_for_instantiation

klass->check_valid_for_instantiation可以防止抽象类被实例化;

3.klass->initialize

klass->initialize 只要是用来初始化 klass 对象,实现如下:

img

如果BBB的 instanceKlass 对象已经初始化完成,则直接返回;否则通过 initialize_impl 方法进行初始化,整个初始化算法分成11步,具体实现如下:

step1

通过 ObjectLocker 在初始化之前进行加锁,防止多个线程并发初始化。

img

step2

如果当前 instanceKlass 处于 being_initialized 状态,且正在被其它线程初始化,则执行 ol.waitUninterruptibly 等待其他线程完成后通知。

img

step3

如果当前 instanceKlass 处于 being_initialized 状态,且被当前线程初始化,则直接返回。
img

其实对于这个step的处理我有疑问,什么情况会走到这一步?经过RednaxelaFX大大提点,如下情况会执行step3:
例如,A类有静态变量指向一个new B类实例,B类里又有静态变量指向new A类实例,这样外部用A时要初始化A类,初始化过程中又要触发B类初始化,B类初始化又再次触发A类初始化。

step4

如果当前 instanceKlass 处于 fully_initialized 状态,说明已经初始化完成,则直接返回;

img

step5

如果当前 instanceKlass 处于 initialization_error 状态,说明初始化失败了,抛出异常。
img

step6

设置当前 instanceKlass 的状态为being_initialized;设置初始化线程为当前线程。
img
如果当前 instanceKlass 不是接口类型,并且父类不为空,且还未初始化,则执行父类的初始化。
img

step8

img

通过this_oop->call_class_initializer方法执行静态块代码,实现如下:

img

this_oop->class_initializer()可以获取静态代码块入口,最终通过JavaCalls::call执行代码块逻辑,再下一层就是具体操作系统的实现了。

step9

如果初始化过程没有异常,说明 instanceKlass 对象已经初始完成,则设置当前 instanceKlass 的状态为 fully_initialized,最后通知其它线程初始化已经完成;否则执行step10 and 11。

img
step10 and 11

如果初始化发生异常,则设置当前instanceKlass的状态为 initialization_error,并通知其它线程初始化发生异常。
img

4.klass->allocate_instance

如果 instanceKlass 初始化完成,klass->allocate_instance会在堆内存创建 instanceOopDesc 对象,即类的实例化;

instanceOopDesc

当在Java中new一个对象时,本质是在堆内存创建一个instanceOopDesc对象。
img

instanceOopDesc 在实现上继承自 oopDesc,其中oopDesc定义如下:

img

当然,这只是 oopDesc的部分实现,oopDesc包含两个数据成员:_mark 和 _metadata。

  • _mark是markOop类型对象,用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致
  • _metadata是一个联合体,其中 wideKlassOop 和 narrowOop 都是指向 InstanceKlass 对象的指针,wide 版是普通指针,narrow版是压缩类指针(compressed Class pointer)

instanceOopDesc对象的创建过程

img

instanceOopDesc对象通过instanceKlass::allocate_instance进行创建,实现过程如下:

1)has_finalizer判断当前类是否包含不为空的finalize方法;
2) size_helper确定创建当前对象需要分配多大内存;
3)CollectedHeap::obj_allocate从堆中申请指定大小的内存,并创建instanceOopDesc对象,实现如下:
img

4)如果当前类重写了 finalize 方法,且非空,需要把生成的对象封装成 Finalizer 对象并添加到 Finalizer 链表中,对象被 GC 时,如果是 Finalizer 对象,会将对象赋值到 pending 对象。Reference Handler线程会将 pending 对象 push 到 queue 中,Finalizer 线程poll 到对象,先删除掉 Finalize r链表中对应的对象,然后再执行对象的 finalize 方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值