JVM虚拟机中创建对象的底层实现

在java的类中,创建一个对象最简单的就是可以直接new一下,然后就可以获取到这个对象的返回值,我们大家都很清楚,new的一下,会在堆中开辟一块空间,然后这个返回值是对象在堆里面的地址的引用,但是对象的实现细节,我们就在这详细的记录下。 

一:对象的内存布局

1.对象在内存中的布局存放可以分为三部分,第一部分叫对象头。对象头中又分为了两块,第一块是存储对象自身的运行时数据的。运行时数据是什么呢?比如HashCode   GC分代年龄  锁状态标识,线程持有的锁,偏向线程Id  偏向时间戳等这些都是运行时数据。这一部分的数据的长度在32位和64位的虚拟机中是不一样的,长度分别是32bit 64bit 。但是由于对象头的这一部分的数据存放也是很多的东西的,这点内存是不够的,所以这一块的区域被设置为非固定大小。此区域会按照对象的状态自动去复用内存。对象头的第二部分就是指针了,对象指向它的类元数据的指针,简单的说就是虚拟机通过这个指针来确定这个对象是哪个类的实例。这里有个特殊的情况,如果是数组的话,他还需要有一块内存是存放这个数组的长度,虚拟机可以通过java对象的元数据判断出类的大小,但是数组的元数据不可以。

2.第二部分是:实例数据,实例数据简单的说就是存储真正的有效信息了,就是我们在类中写的各种类型字段内容,无论是父类还是子类都需要记录下来,这里涉及到了这些字段的存储顺序,他的依据是分配策略和字段顺序。字节相同的会在一起,比如long double字节数都是8.就会分配在一起,入过出先了父类,父类的字段在前面,但是这里有个参数就是CompactFields。意思是紧凑字段的意思,当这个参数为true的时候,表示开始,默认就是true,如果是true,那么当子类中有较为狭窄的字段时候,也会和父类的字段出现在一起,大白话就是,我小,我可以和你们挤挤。

3.第三部分就是对其填充了。这一部分没有任何实际的东西,只有一个目的,就是为了让对象地址值必须满足8的字节的整数倍这个要求,这个是VM的自动内存管理系统要求对象满足的条件。

二:在将对象创建的各个部位保存说完之后,就剩下调用了,对象的调用我们知道的是栈中保存了对象在堆中的地址引用,然后直接一个指针过去就能找到,但是真实的却没有这么简单。

1.在栈中,有一个数据叫做reference.就是通过这个对象来操作的堆中的数据,由于JVM之规范中,reference类型只是规定了一个指向对象的引用,并没有去说如何通过哪种方式去定位去访问堆中的对象的具体位置,所以这方向还需要有虚拟机实现。目前主流的一共有两种,1.使用句柄   2.直接指针

 如果是句柄的话,那么就会在堆中开辟出一块空间,作为句柄池,refercnce中存储的就是句柄的地址,而句柄中又包含了对象实例数据以及对象类型数据。

如果是直接指针的话,那么堆中在布局时候就必须考虑清楚如何放置访问类型数据的相关信息了。而reference中存放的就是对象的直接地址。

这两种都各有利弊,对于句柄的而言,好处就是当虚拟机修改了对象的位置时候,reference不需要改,只是句柄池中的指向改了,栈是不需要动的。但是直接引用的话,速度肯定是快,不需要去走句柄池的这中转的一步,只能说是各有利弊。

猜你喜欢

转载自blog.csdn.net/jack_user_admin/article/details/87920497