当前位置: 首页 > 科技观察

JVM源码分析中Java对象的创建过程

时间:2023-03-14 15:37:47 科技观察

本文将基于HotSpot的实现,深入分析Java对象的创建过程。定义两个简单的类AAA和BBB,通过“javap-cAAA”查看编译后的字节码,如下:新指令的处理过程:1、池为AAA的常量池。这时候AAA这个类已经加载到虚拟机中了。new指令后的#2表示BBB类全限定名的符号引用在常量池的位置;2、方法pool->klass_at负责返回BBB对应的klassOop对象,实现如下:如果常量池中指定位置(#2)的数据已经是oop类型,则表示说明BBB的类已经加载解析完毕,直接通过(klassOop)entry.get_oop()返回klassOop;否则,表示第一次使用BBB,需要解析BBB的符号引用,加载BBB的类,生成对应的instanceKlass对象,在类中相应位置的符号引用应该更新常量池;3、klass->check_valid_for_instantiation可以防止抽象类被实例化;4、klass->initialize实现如下:如果BBB的instanceKlass对象已经初始化,则直接返回;否则,它将通过initialize_impl方法进行初始化。整个初始化算法分为11步,具体实现如下:step1在初始化前使用ObjectLocker进行加锁,防止多个线程并发初始化。step2如果当前instanceKlass处于being_initialized状态,正在被其他线程初始化,则执行ol.waitUninterruptibly等待其他线程完成并通知。step3如果当前instanceKlass处于being_initialized状态,被当前线程初始化,则直接返回。其实我对这一步的处理是有疑虑的。走到这一步会是什么情况?RednaxelaFX做了一个大点之后,step3会在以下几种情况下执行:比如A类有一个静态变量指向一个新的B类实例,B类有一个静态变量指向一个新的A类实例,那么当外部使用A,必须初始化A类,初始化过程再次触发B类初始化,B类初始化又触发A类初始化。step4如果当前instanceKlass处于fully_initialized状态,说明已经初始化完成,直接返回;step5,如果当前instanceKlass处于initialization_error状态,说明初始化失败,抛出异常。Step6设置当前instanceKlass的状态为being_initialized;将初始化线程设置为当前线程。如果当前instanceKlass不是接口类型,且父类不为空且尚未初始化,则执行父类的初始化。step8通过this_oop->call_class_initializer方法执行静态块代码,实现如下:this_oop->class_initializer()可以获得静态代码块的入口,最后通过JavaCalls::call执行代码块逻辑,而下一层是具体操作系统的实现。step9如果初始化过程没有异常,说明instanceKlass对象已经初步完成,然后将instanceKlass的当前状态设置为fully_initialized,最后通知其他线程初始化完成;否则,执行step10和11。Step10和11如果初始化出现异常,将当前instanceKlass状态设置为initialization_error,通知其他线程初始化出现异常。5、如果instanceKlass初始化完成,klass->allocate_instance会在堆内存中创建一个instanceOopDesc对象,即类的实例化;Java中instanceOopDesc是new对象的时候,其实质就是在堆内存中创建一个instanceOopDesc对象。instanceOopDesc在实现上继承自oopDesc,oopDesc定义如下:当然,这只是oopDesc的部分实现,oopDesc包含两个数据成员:_mark和_metadata。1、_mark是一个markOop类型的对象,用来存储对象本身的运行时数据,比如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳、等,占用内存的大小与虚拟机的位长一致。更具体的实现可以看《java对象头的HotSpot实现分析》2。_metadata是一个联合体,其中wideKlassOop和narrowOop都是指向InstanceKlass对象的指针。宽版是普通指针,窄版是压缩类指针(compressedClasspointer)的创建过程instanceOopDesc对象instanceOopDesc对象是通过instanceKlass::allocate_instance创建的,实现过程如下:1.has_finalizer判断当前类是否包含不为空的finalize方法;2.size_helper判断当前对象需要分配多少内存;3、CollectedHeap::obj_allocate从堆中申请指定大小的内存,并创建一个instanceOopDesc对象,如下:4、如果当前类重写了finalize方法且不为空,则需要封装生成的对象放入一个Finalizer对象中,并将其添加到Finalizer链表中。当对象被GC时,如果是Finalizer对象,该对象会被分配给pending对象。ReferenceHandler线程会将挂起的对象推入队列,Finalizer线程会轮询对象,先删除Finalizer链表中对应的对象,然后执行对象的finalize方法;