2.Java堆对象分配,布局和访问的全过程

时间:2023-01-02 19:58:39

对象的创建

       这里说的对象指的是普通Java对象,不包括数据和Class对象等。

       类加载过程:虚拟机遇到new指令,首先去检查new指令的参数是否能在常量池中定位到一个类的符号引用,并且这个符号引用代表的类是否已被加载,解析和初始化过。如果没有,那么先执行相应的类加载过程。

       堆内存分配:在类加载检查通过后,要创建的对象所需的内存大小已经确定了,虚拟机将一块确定大小的内存从Java堆中划分出来分配给对象。

       假设Java堆中,所有用过的内存放在一边,空闲的内存放在另外一边,中间放一个指针作为分界点的指示器,那分配内存就是移动这个指针的过程。这种内存分配方式叫“指针碰撞”。

       但如果Java堆中的内存,空闲的和使用过的是交互相错的,不是规整的,那么虚拟机必须维护一个队列,记录哪些内存块是可用的,分配的时候,查找表找到一块足够大的空间划分给对象,并更新表信息。这种方式叫“空闲列表”。

       选择哪种分配方式是由Java堆是否规整决定的,而Java堆是否规则是由所采用的垃圾收集器是否带有压缩整理功能决定的。

       还需要考虑的一个问题是,在虚拟机中创建对象是否是一个非常频繁的行为。即便是采用“指针碰撞”的方式,在并发的情况下,修改指针也不是线程安全的。

       对于这个问题,有两种解决方法,第一,对分配内存空间的操作进行同步处理--实际上虚拟机采用CAS配上失败重试的方式保证了更新操作的原子性;第二,每个线程在Java堆中预先分配一小块内存,叫本地线程分配缓冲(TLAB)。哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。

       虚拟机是否使用TLAB,可以通过-XX:+/_UseTLAB参数来设定。

       内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),如果使用TLAB,这一过程也可以提前至TLAB分配时进行。这一操作保证了对象的实例字段在Java代码中可以不赋初始值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值。

       接下来,虚拟机要对对象进行必要的设置,例如这个对象属于哪个类,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等信息。这些信息存在对象的对象头(Object Header)中。虚拟机根据当前的运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

       到现在为止,从JVM的角度来看,一个新的对象已经产生了。但从Java程序来看,还需要对其执行init方法,所有字段都还是零值。一般来说,执行new指令之后会接着执行init方法,把对象安装程序员的意愿进行初始化。

对象的内存布局

       在JVM中,对象在内存中存储的布局分为3个区域:对象头(Header),实例数据(Instance Data)和对齐填充(Padding)。

       对象头包括两个部分的信息:第一,用于存储对象自身的运行时数据,如哈希码(HashCode),GC分代年龄,锁状态标志,线程持有的锁,偏向线程IO等。这部分数据在32位和64位的虚拟机当中分别是32bit和64bit的长度。官方称它为“Mark Word”。

       Mark:还需要补充

对象的访问定位

       建立对象是为了使用对象,程序通过栈上的reference数据来操作堆上的具体对象。Java虚拟机规范当中,只规定reference类型是一个指向对象的引用,并没有定义这个引用如何去定位,访问堆中的对象。所以,对象访问方式是取决于虚拟机的具体实现的,目前主流的访问方式有使用句柄和直接直接指针两种

1.如果使用句柄的话,那么Java堆中将划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象的实例数据与类型数据各自的具体地址信息。

2.Java堆对象分配,布局和访问的全过程

2.如果使用直接指针访问,那么Java堆对象的布局中就必须考虑如何访问对象访问类型数据的信息,而reference中存储的就是对象在堆中的地址。

2.Java堆对象分配,布局和访问的全过程

       使用句柄来访问的最大的好处就是reference中存储的是稳定的句柄地址,而对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改。

       使用直接内存访问的最大的好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在Java中是非常频繁的,因此这类开销积少成多之后也是非常可观的。

       Sun HotSpot虚拟机是采用第二种方式进行对象访问的,即直接内存访问。