深入理解Java虚拟机:HotSpot虚拟机对象揭秘

来源:互联网 发布:matlab 符号矩阵 编辑:程序博客网 时间:2024/06/06 14:21

什么是HotSpot?

    提起HotSpot VM,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。但不一定所有人都知道的是,这个目前看起来“血统纯正”的虚拟机在最初并非由Sun公司开发,而是由一家名为“Longview Technologies”的小公司设计的;甚至这个虚拟机最初并非是为Java语言而开发的,它来源于Strongtalk VM,而这款虚拟机中相当多的技术又是来源于一款支持Self语言实现“达到C语言50%以上的执行效率”的目标而设计的虚拟机,

    Sun公司注意到了这款虚拟机在JIT编译上有许多优秀的理念和实际效果,在1997年收购了Longview Technologies公司,从而获得了HotSpot VM。

    HotSpot VM既继承了Sun之前两款商用虚拟机的优点(如前面提到的准确式内存管理),也有许多自己新的技术优势,如它名称中的HotSpot指的就是它的热点代码探测技术(其实两个VM基本上是同时期的独立产品,HotSpot还稍早一些,HotSpot一开始就是准确式GC,而Exact VM之中也有与HotSpot几乎一样的热点探测。为了Exact VM和HotSpot VM哪个成为Sun主要支持的VM产品,在Sun公司内部还有过争论,HotSpot打败Exact并不能算技术上的胜利),HotSpot VM的热点代码探测能力可以通过执行计数器找出最具有编译价值的代码,然后通知JIT编译器以方法为单位进行编译。如果一个方法被频繁调用,或方法中有效循环次数很多,将会分别触发标准编译和OSR(栈上替换)编译动作。通过编译器与解释器恰当地协同工作,可以在最优化的程序响应时间与最佳执行性能中取得平衡,而且无须等待本地代码输出才能执行程序,即时编译的时间压力也相对减小,这样有助于引入更多的代码优化技术,输出质量更高的本地代码。

    在2006年的JavaOne大会上,Sun公司宣布最终会把Java开源,并在随后的一年,陆续将JDK的各个部分(其中当然也包括了HotSpot VM)在GPL协议下公开了源码,
并在此基础上建立了OpenJDK。这样,HotSpot VM便成为了Sun JDK和OpenJDK两个实现极度接近的JDK项目的共同虚拟机。

    在2008年和2009年,Oracle公司分别收购了BEA公司和Sun公司,这样Oracle就同时拥有了两款优秀的Java虚拟机:JRockit VM和HotSpot VM。

    Oracle公司宣布在不久的将来(大约应在发布JDK 8的时候)会完成这两款虚拟机的整合工作,使之优势互补。
整合的方式大致上是在HotSpot的基础上,移植JRockit的优秀特性,譬如使用JRockit的垃圾回收器与MissionControl服务,使用HotSpot的JIT编译器与混合的运行时系统。


一、对象创建

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

    检查通过后,虚拟机为新生对象在堆中分配内存,对象所需要的内存在类加载完成就可完全确定。

  • 如果 堆 中内存是绝对规整的,内存就会分为 空闲用过的两边,中间放着一个指针作为分界点的指示器,分配内存就仅仅是把那个指针向空闲的空间那边挪过一段与对象大小相等的距离,成为 “指针碰撞 (Bump the Pointer)”
  • 如果java堆中内存不是规整的,虚拟机就维护一个列表,记录哪些内存块是可用的,在分配时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表 (Free List)”。选择哪种分配方式由java堆是否规整决定,而java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

    虚拟机需要将分配到的内存空间都初始化为零值,如果使用TLAB(本地线程分配缓冲),这一工作过程也可以提前至TLAB分配时进行。

    虚拟机要对对象进行必要的设置。必要信息都会存放在对象的对象头之中。

    执行new指令之后会接着执行方法,把对象按照程序员的意愿进行初始化。


二、对象的内存布局

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

    对象头包含两部分信息:第一部分用于存储对象自身的运行时数据。这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,称为“Mark Word”;第二部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。如果对象是java数组,那在对象头中还必须有一块用于记录数组长度的数据。
    实例数据存储对象真正的有效信息,就是程序代码中定义的各种类型的字段内容。
    对齐填充并不是必然存在,也没有特别的含义,它仅仅起着占位符的作用。

这里写图片描述


三、对象的访问定位

    对象的访问方式取决于虚拟机实现而定的,目前主流的访问方式有使用句柄和直接指针两种。

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

这里写图片描述

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

这里写图片描述

    使用句柄好处就是对象移动时只会改变句柄中的实例数据指针,而reference本身不需要修改。
使用直接指针访问方式最大好处就是速度更快。

0 0