java虚拟机

java对象的创建与访问

2018-04-05  本文已影响0人  风一样的行者

1.1 对象的创建 (不包含数组跟Class对象)

创建一个对象需要的步骤:

1)检查 。检查new指令包含的参数是否能在常量池中定位到一个类的符号引用,如果有,再检查这个符号引用代表的类是否已经被加载,解析和初始化过,如果是,直接进入下一步操作,如果没有,则先执行相应的类加载操作。

2)分配内存。对象所需要的内存大小在类加载完成之后便可完全确定。在java多线程中 内存分配时可能出现的线程同步问题,解决这个问题的两个主要方法:一是对分配内存空间的动作进行同步处理二是TLAB,把内存分配的动作按照线程分配在不同的空间中进行,即每一个线程分配一小块TLAB,只有TLAB用完分配新的TLAB的时候才需要进行同步锁定。

3)系统初始化。内存分配完成之后,需要将内存空间都初始化为零值(对象头除外),如果使用了TLAB 这一操作会在TLAB分配时进行。这一步操作保证了对象实例字段在java代码中不赋初始值就能直接使用。

4)对象头信息设置。虚拟机对对象进行必要的设置, 如对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄,这些信息存放在对象头中。到这一步 虚拟机中一个对象已经创建完成。

5)执行init指令,初始化赋值 。从java角度来看,对象的创建刚刚开始,方法还没有执行,所有的字段都还为零,new指令之后会执行 init指令,将对象按照程序员的意愿进行初始化,到这一步一个真正能够使用的对象才算完全产生出来。

下面时对象创建时的流程图:

能使用的java对象创建的流程图


1.2 java对象的内存组成 (内存布局)

1)java对象内存主要分为三块区域:对象头,实例数据, 对齐填充

2)对象头由两部分组成,markword,另外一部分是类型指针。markword用于存储对象自身的运行时数据,如哈希码,GC分代年龄,锁状态,线程持有的锁,偏向线程ID等,

为synchronized锁的实现提供了基础。(synchronized 的原理会在后面的章节分析)

    对象头中markword

3)类型指针是指对象指向它的元数据的指针,虚拟机通过这个指针来判断这个对象是哪个类的实例。在数组对象中,对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过元数据的大小来确定java对象的大小,却无法通过数据的元数据大小来确定数组的大小。

4)实例数据。是对象真正存储的有效信息,也就是代码中定义的各个字段的字段内容

5)对齐填充并不是必然存在的,HotSpot虚拟机要求对象的起始地址必须是8字节的整数倍,如果不是8的整数倍就用对齐填充来补全。

1.3 对象的访问

对于对象的访问方式只要取决于虚拟机的实现方式,主要的访问方式有两种:一种为句柄访问,还有一种是直接指针

对于句柄访问   java堆中会划出一块内存作为句柄池,句柄包含了对象实例数据 类型数据 各自的具体地址信息 reference中存放的是对象的句柄地址。

句柄访问的优势在与,在对象呗移动时只会改变句柄中的实例数据指针,而reference的地址不需要改变。

句柄访问对象

对于直接指针访问 ,java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference 中存储的就是对象地址

使用直接指针访问的好处在于速度快,因为节省了一次指针定位的开销。

上一篇下一篇

猜你喜欢

热点阅读