欢迎光临散文网 会员登陆 & 注册

2 HotSpot虚拟机中的对象

2023-04-23 05:29 作者:废品批发  | 我要投稿

2.1 对象的创建

        1. 虚拟机遇到一条new指令时, 会先检查该指令的参数是否能定位到常量池中的类的符号引用, 检查这个符号引用的类是否已经加载、解析、初始化, 如果没有,需要进行类的加载过程.

        2. 类加载完成后, 需要为对象分配内存空间, 对象所需要的内存空间在类加载完成后就完全确定.

分配内存的方式:     

    1. 指针碰撞:如果堆内存绝对规整,已使用的内存放在一边,未使用的放在另一边,分配新内存就是移动分界点的指示器.     

    2. 空闲列表:如果堆内存不规整,需要维护一个列表记录未使用的内存区域,查找分配一个足够大区域给新对象,然后更新列表.堆是否规整取决于垃圾回收器是否具有压缩整理功能.

分配内存的过程如何避免并发安全问题:     

    1.对分配内存的过程进行加锁 (虚拟机通过CAS加上失败重试保证更新操作的原子性).     

    2.TLAB(可通过参数开启):提前按照线程分配不同的堆空间, 称为本地线程分配缓冲.哪个线程需要分配对象内存就在哪个线程中分配,只有当TLAB用尽, 产生新的TLAB时才需要加锁. 对象内存空间分配完成后, 会将该空间置为零值, 若果需要TLAB, 该过程提前到TLAB分配时.此时对象的属性已经被赋予零值.        

        3. 对象的初始化, 对象已经产生, 但由于没有调用构造方法, 此时的对象还不是我们想要的.等到<init>方法调用完成该对象才算结束创建, 并可真正的被使用.

2.2 对象的内存布局

堆中的对象包含3部分:

1. 对象头:        

    - Mark Word        

    - 类信息的指针:不一定有, 这取决于对象的访问方式, 使用句柄池进行访问的话, 对象的对象头是不会去存储指向方法区中类型数据的指针的, 详情在下文的2.3可见.      

    - 数组长度:不一定有, 当堆中存储的是数组对象时,会有该部分. 

2. 实例数据:对象真正存储的有效数据, 各种类型字段(包括父类继承的) 

3. 对齐填充:该部分并不是必须的, 起到占位符的作用, hotspot要求对象占用的空间需要是8个字节的整数倍,对象头正好符合, 如果实例数据没有对齐, 就通过对齐填充补全.

下图是mark word在64位虚拟机中, 在不同锁状态的记录.

64位mark word

关于不同的锁状态, 涉及Java中锁的膨胀, 以后会提这件事.

2.3 对象的访问定位

栈中的引用型变量需要定位到堆中的对象,

  1. 使用句柄

    在堆中创建句柄池, 在句柄池中拥有对象实例数据指针和对象类型数据指针, 对象实例数据指针指向堆中的对象, 对象类型指针指向方法区中对象的类型信息.

  2. 直接访问

    直接访问时, 堆中不需要创建句柄池, 栈中引用型变量直接指向堆中的对象, 在对象中的对象头里存储指向对象所属类型的指针, 用来访问方法区的类型数据.

句柄池访问
直接访问

两种访问方式各有优势, 使用句柄池虽然会多一次指针定位的开销, 但是当堆中对象位置发生改变时, 只需要改变句柄池中对象的引用, 不需要改动栈中局部变量表的reference变量的指向; 直接访问的方式与之优势相反.HotSpot使用的是直接访问的方式.


2 HotSpot虚拟机中的对象的评论 (共 条)

分享到微博请遵守国家法律