2.3 HotSpot虚拟机对象的探秘
以常用的HotSpot虚拟机和常用的内存区域Java堆为例,探讨虚拟机在Java堆中的对象分配,布局和访问的全过程:
对象的创建,对象的内存布局,对象的访问定位。
1.对象的创建
1-文中讨论的对象,仅限于普通Java对象,不包括数组和Class对象
2-对象的创建过程:
1)虚拟机遇到一条new指令时
-判断常量池是否能定位到一个类的符号引用
-如果存在,则检查这个符号引用代表的类是否被加载,解析和初始化
-如果没有被加载,则执行类加载过程
2)类加载检查通过之后,接下来虚拟机就为新生对象分配内存。
-对象所需的内存大小在类加载完成之后是确定的,为对象分配空间等同把一块大小确定的内存空间从Java堆中划分出来。
-指针碰撞
1)假设Java堆内存是绝对规整的,所用过的内存都在放一边,空闲内存放在另一边,中间放着一个指针作为分界点的指示器。
2)那所分配的内存仅仅是把指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”。
-空闲列表
1)如果Java堆的内存不是规整的,已使用的内存和空闲内存相互交错,虚拟机就必须维护一个列表,记录上哪些内存块是可用的。
2)在分配内存的时候,从列表中找到一块足够大的空间分配给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”。
-选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
3-除了如何划分可用空间之外,还有一个需要考虑的问题是对象的创建在虚拟机中是非常频繁的,在并发条件下并不是线程安全的。
解决方法有两种:
1)一种是对分配内存动作做同步处理
-实际上虚拟机使用CAS配上失败重试保证更新操作的原子性
2)另一种是把内存分配动作按照线程划分在不同的空间之中进行
-即每个线程在Java堆中划分一小块内存,称为本地线程分配缓冲(TLAB),哪个线程要分配内存,就在哪个TLAB上分配,只有TLAB使用完并分配新的TLAB时,才需要同步操作。虚拟机是否使用TLAB,可以通过参数来设定
4-内存分配完成后,虚拟机要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在Java代码中不需要赋初始值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值
5-接下来,虚拟机要对对象头进行必要的设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等信息
6-在上面的工作完成之后,从虚拟机视角上看,一个新的对象已经产生了;但是从Java程序的视角上看,对象才刚刚创建,<init>方法还没执行,所有字段都还为零。
一般来说,执行new指令之后会接着执行<init>方法,把对象按照程序员的意向进行初始化。
2.对象的内存布局
1-在HotSpot虚拟机中,对象的内存布局可分为3块区域:对象头,实例数据,对齐填充
1)对象头
HotSpot虚拟机对象头包括两部分信息:
-第一部分用于存储对象自身的运行时数据
-第二部分是类型指针,即对象指向它的类元数据的指针
-虚拟机通过这个指针确定这个对象是哪个类的实例
-如果对象是一个Java数组,那在对象头还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息来确定对象的大小,而无法从数组的元数据中确定数组的大小
2)实例数据
真正存储对象的有效信息
3)对齐填充
占位符的作用。由于HotSpot VM 要求对象的起始地址必须是8字节的倍数,也就是对象的大小必须是8字节的倍数,对象头正好是8字节的倍数,因此,当实例数据不足8字节倍数时,就需要通过对齐填充来补齐。
3.对象的访问定位
1-Java程序通过栈上的reference数据来操作堆上的具体对象。
2-由于reference类型虚拟机规范中只规定了一个指向对象的引用,所以对象的访问取决于虚拟机的实现
3-目前主流的访问方式有:使用句柄和直接指针两种。
1)句柄
-如果使用句柄访问的话,Java堆中将会划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址;
-句柄池包含了对象的实例数据和类型数据各自的地址信息。
2)直接指针
-如果使用直接指针访问,那么Java堆对象的布局就必须要考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象的地址
4-这两种访问方式的区别:
1)使用句柄访问最大的好处是reference存储的是稳定的句柄地址,在对象被移动时(垃圾收集时),只会改变句柄中的实例数据指针,而reference本身不需要修改。
2)使用直接指针最大的好处就是访问速度更快,它节省了一次指针定位的时间开销。
5-Sun HotSpot虚拟机使用直接指针方式来进行对象的访问
相关推荐
- Java堆是所有线程共享的一块内存区域,主要用来存放对象实例。 - 包括了所有的对象实例和数组。 - 垃圾收集器的主要管理区域,负责清理不再使用的对象。 - 物理上可以是不连续的内存空间,但在逻辑上被视为...
#### 一、Java内存区域与内存溢出异常 Java虚拟机(JVM)在运行过程中会管理多种不同的内存区域,这些区域各自承担着特定的任务,并且每种区域都有可能发生内存溢出异常。 ##### 1.1 程序计数器(Program Counter ...
第2章 Java内存区域与内存溢出异常 2.1 概述 2.2 运行时数据区域 2.2.1 程序计数器 2.2.2 Java虚拟机栈 2.2.3 本地方法栈 2.2.4 Java堆 2.2.5 方法区 2.2.6 运行时常量池 2.2.7 直接内存 2.3 HotSpot...
#### HotSpot虚拟机对象探秘 **对象的创建:** 对象的创建涉及类加载、内存分配、初始化等多个步骤。类加载确保类的结构信息已经加载到方法区;内存分配为对象分配内存空间;初始化则对对象进行必要的设置,使其可用...
HotSpot虚拟机的对象探秘涉及对象的创建、布局和访问定位。对象在JVM中创建,首先通过类加载器加载相应的类信息到方法区,然后在Java堆中分配内存,之后进行对象初始化。 #### 内存溢出异常 内存溢出异常指的是...