首页 > 编程知识 正文

Java对象深拷贝工具类,头歌java入门变量与数据类型答案

时间:2023-05-05 06:47:14 阅读:129806 作者:1737

原始声明:正文为公众号【猿灯塔】原创,转载请说明出处标识

《365篇原创项目》第十一篇。

今天! 灯塔君对大家说:

JVM源代码分析的Java对象头实现

在HotSpot虚拟机中,对象内存中的布局分为三个区域:对象标头、实例数据和对齐填充。

对象磁头

对象标头由两部分组成: nrdqt Word和类型指针。

nrdqt Word

nrdqt Word用于存储对象自身的运行时数据,如哈希代码(HashCode )、GC世代年龄、锁定状态标志、线程具有的锁定、偏转线程ID、偏转时间戳等

类型指针

类型指针是指虚拟机确定对象是哪个类的实例的对象的类元数据。

实现熏制咖啡豆Oop

HotSpot以醉熏咖啡豆Oop类型实现nrdqt Word,具体可在醉熏咖啡豆Oop.hpp文件中找到。

由于需要存储对象的运行时数据很多,考虑到虚拟机的内存使用量,醉熏咖啡豆Oop被设计为非固定的数据结构,以便在非常小的空间中存储尽可能多的数据,根据对象的状态复用自己的存储空间

hash :要保存的哈希代码

age :保存对象的各世代年龄

biased_lock :偏转锁定识别位

lock :锁定状态识别位

JavaThread:*保存具有偏转锁定的线程ID

epoch :保存偏转时间戳

熏咖啡豆Oop :里面不同片的标志位,代表不同片的状态:

存储着不同的锁定状态、不同的数据:

熏制咖啡豆Oop提供了许多用于确定当前目标头状态和更新目标头数据的方法,为同步锁定的实现提供了基础。

让我们看看代码:

首先,定义两个简单的类AAA和BBB

通过` ` javap -c AAA ` ` `查看编译后的字节码,具体为:

Java的new关键字对应于jvm的new指令,在InterpreterRuntime类中定义,并按如下方式实现:

new指令的实现过程:

1、这里,pool是AAA的常数pool。 此时,AAA的class已经加载到虚拟机中,new指令后的#2表示BBB类的全限定名的符号被引用到常数池的位置;

2、方法pool-klass_at负责返回支持BBB的klassOop对象,实现如下:

如果常量池中指定位置#2的数据已经为oop类型,指示BBB的class已加载并已解析((klassOop ) entry.get_oop ) ),则直接返回klassOop。 否则,这意味着您将首次使用BBB,需要分析BBB的元件引用,加载BBB的class类,生成相应的instanceKlass对象,并更新constant pool相应位置的元件引用

3、klass-check _ valid _ for _ instantiation可以防止抽象类被实例化;

4、klass-initialize的实现如下。

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

步骤1

通过在初始化之前锁定对象锁定器来防止同时初始化多个线程。

步骤2

步骤3

如果当前实例类处于being_initialized状态且已在当前线程上初始化,则直接返回。

其实我对这个step的处理有疑问。 什么情况会到此为止呢? 经过RednaxelaFX的大点,在以下情况下执行step3:

例如,a类具有指向new B类实例的静态变量,而b类具有指向new A类实例的静态变量。 这样,如果在外部使用a类,则初始化a类,在初始化过程中开始初始化b类,然后b类初始化再次开始初始化a类。

如果当前实例类处于fully_initialized状态,指示初始化完成,则直接返回;

步骤5

如果当前实例类处于initialization_error状态,请说明初始化

失败了,抛出异常。

step6

设置当前instanceKlass的状态为 being_initialized;设置初始化线程为当前线程。

如果当前instanceKlass不是接口类型,并且父类不为空,且还未初始化,则执行父类的初始化。

step8

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

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

step9

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

step10 and 11

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

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

instanceOopDesc

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

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

当然,这只是 oopDesc的部分实现,oopDesc包含两个数据成员:_醉熏的咖啡豆 和 _metadata。

1、_醉熏的咖啡豆是醉熏的咖啡豆Oop类型对象,用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致,更具体的实现可以阅读 《java对象头的HotSpot实现分析》

2、_metadata是一个联合体,其中wideKlassOop和narrowOop都是指向InstanceKlass对象的指针,wide版是普通指针,narrow版是压缩类指针(compressed Class pointer)

instanceOopDesc对象的创建过程

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

1、has_finalizer判断当前类是否包含不为空的finalize方法;

2、size_helper确定创建当前对象需要分配多大内存;

3、CollectedHeap::obj_allocate从堆中申请指定大小的内存,并创建instanceOopDesc对象,实现如下:

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

365天干货不断微信搜索「猿灯塔」第一时间阅读,回复【资料】【面试】【简历】有我准备的一线大厂面试资料和简历模板

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。