深刻理解java虚拟机——HotSpot虚拟机对象

1.对象的建立java

①虚拟机遇到一条new指令是,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,而且检查这个符号引用表明的类是否已被加载、解析和初始化过。若是没有必须先加载类。(后续再补充)数组

②接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后即可彻底肯定。为对象分配内存的方式能够分为在连续内存空间上的的“指针碰撞”方式和在不连续内存空间的“空闲列表”方式。分配对象内存空间时即便仅仅修改指针所指向的位置,在并发状况下也不是线程安全的,两种解决方案:一种对分配内存空间的动做进行同步处理——实际上虚拟机采用CAS配上失败重试的方式保证更新操做的原子性;另外一种是把内存分配的动做按照线程划分在不一样的空间之中进行,即每一个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。安全

③内存分配完成后,虚拟机须要将分配到的内存空间都初始化为零值(不包括头对象)。若是使用TLAB能够提早至分配TLAB时进行数据结构

④接下来虚拟机要对对象进行必要的设置,例如对象是哪一个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息放在对象头中。并发

2.对象的内存布局布局

对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。spa

HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。对象须要存储的运行时数据不少,其实已经超出了3二、64位Bitmap结构所能记录的限度,可是对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽可能多的信息,它会根据对象的状态复用本身的存储空间。例如在32位的HotSpot虚拟机中对象未被锁定的状态下,Mark Word的32个Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代年龄,2Bits用于存储锁标志位,1Bit固定为0,在其余状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容以下表所示。线程

存储内容设计

标志位指针

状态

对象哈希码、对象分代年龄

01

未锁定

指向锁记录的指针

00

轻量级锁定

指向重量级锁的指针

10

膨胀(重量级锁定)

空,不须要记录信息

11

GC标记

偏向线程ID、偏向时间戳、对象分代年龄

01

可偏向

 

对象头的另一部分是类型指针,便是对象指向它的类元数据的指针,虚拟机经过这个指针来肯定这个对象是哪一个类的实例。并非全部的虚拟机实现都必须在对象数据上保留类型指针,换句话说查找对象的元数据信息并不必定要通过对象自己。另外,若是对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,由于虚拟机能够经过普通Java对象的元数据信息肯定Java对象的大小,可是从数组的元数据中没法肯定数组的大小。

3.对象的访问定位

java程序须要经过栈上的reference数据来操做堆上的具体对象。因为reference类型在java虚拟机规范中只规定了一个指向对象的引用,并无定义这个引用用何种方式去定位、访问堆中的对象的具体位置,因此它取决于虚拟机的实现。目前主要有句柄和直接指针两种。

这两种方式各有优点,句柄的好处是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是广泛的行为)时只会改变句柄中的实例数据指针,而reference自己不须要修改。

使用直接指针的方式好处就是速度更快,它节省了一次指针定位的时间开销,因为对象的访问在java中很是频繁,所以优点也很大。HotSpot使用直接指针的访问方式。

相关文章
相关标签/搜索