美文网首页
第二章(二) HotSpot虚拟机探秘

第二章(二) HotSpot虚拟机探秘

作者: 靈08_1024 | 来源:发表于2018-07-02 12:57 被阅读12次

我们都知道HotSpot虚拟机现在是Java官方虚拟机。本节了解HotSpot虚拟机的一些内部信息。

对象的创建

在Java中创建一个对象(泛指java Bean对象,不包括数组和Class对象等),虚拟机接收到一条new指令,首先在常量池中找类的符号引用,并检查其是否已被加载、解析、初始化过。若没有则必须执行。
在类加载检查通过后,接下来虚拟机为新生对象分配内存。对象所需的内存大小在类加载完成后便可以完全确定。
其分配内存的方式有两种:

  • 指针碰撞(Bump the Pointer):限于Java堆内存绝对规整。将分界点指针向空闲区域移动所需大小。
  • 空闲列表(Free List):Java堆内存不规整。此时VM会维护一个列表,记录哪些内存可用。找到足够可用的空间划分给对象实例,并更新该列表。

ps:Java堆内存规整,即存活的在一边,空闲的在一边。中间会有一个指针作为分界点。
VM采用哪种方式取决于GC算法,在使用Serial、ParNew等待Compact过程的收集器时,系统采用指针算法;而在使用CMS这种基于Mark-Sweep算法的收集器时,采用空闲列表

在划分内存时,通常会出现并发状况,JVM的解决方案有二:
方案一:
采用CAS+retry来保证操作的原子性;
方案二:
TLAB方式。

ps:TLAB(Thread Local Allocation Buffer),本地线程分配缓冲。即每个线程预先分配一些内存,在使用完之后需要新TLAB时,才会进行同步锁定。相关参数:-XX:+/-UseTLAB决定是否使用;-XX:TLABWasteTargetPercent设置TLAB大小。
//TODO TLAB 与零值。

对象的内存布局

在HotSpot虚拟机中,对象在内存中的存储分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

对象头
对象头包括存储对象自身的运行时数据类型指针
运行时数据,如哈希码、GC分带年龄、锁标志位、线程持有的锁、偏向线程ID、偏向时间戳等,这部分在32位和64位JVM中分别为32bit和64bit。
类型指针,即对象指向他的类元数据的指针,虚拟机通过这个指针来确定对象所属类的实例。
如果对象是一个数组,在对象头中还有一块记录数组长度的数据。因为VM可以通过JOPO的元数据确定JAVA对象的大小,not for arrays。

实例数据
实例数据是对象真正有效的存储信息。包含代码中定义的各种类型的字段内容,无论是父类中继承的还是子类中定义的,都会记录。
VM会将相同的长度的字段分到一起,如long和double会分到一起。若CompactFields参数的值为true(默认true),那么子类中较窄的变量会存在父类的间隙中。

对齐填充
该部分不是必须,仅仅是占位符的作用。
由于HotSpot VM的自动内存管理系统要求对象的起始地址是8字节的整数倍,即对象的大小必须是8字节的整数倍。而对象头恰好是8字节的倍数,因此,当实例部分没有对齐时,需要通过对齐来补全。

对象的访问定位

我们都知道Java中的对象是通过reference数据来进行访问的。关于对象的访问,VM目前有两种实现方式:句柄访问直接访问

句柄访问

通过句柄访问对象.png

使用句柄访问,在Java堆中会划分出一块内存池作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了 对象实例数据类型数据 各自的具体信息。

直接访问
HotSpot采用的直接访问的方式。

通过直接指针访问对象.png

比较
这两种方式各有优势。
句柄访问的优势,就是reference中存储的就是句柄地址,在对象被移动(如GC)时,只会改变句柄池中的数据指针,reference是不受影响的。
直接访问的优势,即速度快,节省了一次指针定位的开销。
因为对象的访问在Java中是非常频繁的,所以HotSpot采用了直接访问的方式。

下一节:OOM异常

相关文章

网友评论

      本文标题:第二章(二) HotSpot虚拟机探秘

      本文链接:https://www.haomeiwen.com/subject/ckzcuftx.html