Hotspot虚拟机对象揭秘
2018-03-31 本文已影响0人
jqdywolf
Hotspot虚拟机对象揭秘
这部分我们主要分三部分:对象的创建、对象的内存布局、对象的访问定位。这里说的都是堆。
对象的创建
虚拟机遇到new指令:
- 检查类是否已被加载
检查指令的参数是否可以在常量池中定位到一个类的符号引用,且检查这个符号引用代表的类是否已被加载。如果没有执行类加载过程。 - 为生成的对象分配内存
对象的大小在类加载后已被确定。
目前主流的是两种分配方式:指针碰撞和空闲列表。
指针碰撞:即堆内存是规整的,分配内存就是移动临界指针而已。
空闲列表:对内存不是规整的,虚拟机维护一个空闲内存列表。
具体哪种方式由堆采用的GC是否带有压缩整理功能决定。这里引出一个多线程下线程安全问题:多个线程同时分配内存时,可以出现同一块内存分配给多个对象。如何解决?
两种方案:使用CAS操作和本地线程分配缓冲。
CAS就不说了,本地线程分配缓冲:就是说堆中都会为每个线程预先分配一小块内存(TLAB),当需要生成对象时,先使用这块内存,用完了再使用其他堆内存(需要同步锁定)。 - 将内存空间初始化零值
- 设置对象头的信息
- 执行init方法,即构造器方法。
对象的内存布局
即堆中每个对象的内存布局。
我们都知道分为三部分:对象头+实例数据+填充数据。
对象头=Mark Word + 指向元数据指针 (+ 数组长度)
2/3个虚拟机位数长度
Mark Word:GC分代信息+HashCode+锁信息。
上面都已经是我们知道的。
注意的两点:
- 实例数据的布局
Hotspot默认布局:相同宽度的字段总被分配到一起。(long/double int short/char byte)在满足这个前提下,父类出现在子类前面。 - 填充数据
Hotspot虚拟机要求对象的起始地址必须是8字节(64 bit)的整数倍。即对象的大小必须是8字节的整数倍。
对象的访问定位
即栈中的引用如何找到堆中对象以及方法区中的元数据。
两种方式:句柄访问和直接指针。
-
句柄访问
句柄访问 -
直接指针
指针指针
句柄访问的优点:对象被移动,引用无感知
直接指针的优点:相比较于句柄访问少了一次内存访问。效率高。HotSpot用的是这个。