JAVA虚拟机之对象存储细节(2)

HotSpot虚拟机对象机制分析

以HotSpot JVM和常用的java heap为例,讨论对象分配、布局、访问的全过程.

对象的创建

  1. 虚拟机遇到new时,检这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个类是否被加载、解析、初始化过,若没有,则加载这个类。
  2. 类加载检查通过后,确定所需内存大小,将这块内存从JAVA堆中划分出来。这里有两种划分方法:指针碰撞(Bump the Pointer) 、空闲列表(Free List)。选择哪种分配方式是根据java堆是否规整来确定的。而是否规整又决定了所采用的GC是否带有压缩整理功能。(这部分的内容就像是操作系统的内存管理,固定大小或动态分配,动态分配的话就设计到内存碎片的压缩。)
    • 指针碰撞 若java堆中的内存是绝对规整的(内存块大小都相同),那么把指针移动与对象大小相同的距离即可实现内存分配。
    • 空闲列表 java堆中的内存不是规整的,虚拟机要自己维护一个列表来记录哪块内存是可用的,分配时需要找到可用大小的内存块并更新列表。
  3. 对象创建的频繁性也是需要考虑的内容,即便是修改指针指向位置,在并行情况下也不是线程安全的,可能出现正在个对象A分配内存,指针还没来得及修改,对象B又使用了原来的指针来分配内存的情况。解决方案有两种:
    • CAS配上失败重试的方法保证更新的原子性
    • 把内存分配的动作按照线程划分在不同的空间中进行。即每个线程预先分配一小块内存,称为TLAB,哪个线程分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并重新分配新的TLAB时,才需要同步锁定。
  4. 分配完成后需要初始化内存空间为零值。
  5. 再接下来,JVM对对象进行必要设置,例如对象是哪个类的实例、如何找到元数据信息、对象哈希码、GC分代年龄等信息,这些信息存在对象头(Object header)中。
  6. 以上步骤后,虚拟机层面对象已经创建,但是JAVA层面方法还没执行,所有字段还都是0。接下来执行方法

对象的内存布局

在HotSpotJVM中,存储对象的布局可以分为三块区域:对象头(Header),对象示例数据(Instance Data)和对象填充(Padding)。

  • 对象头
    对象头分两部分:
    • 一部分存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态、线程持有的锁、偏向线程ID、偏向时间戳。这部分只占32bit或者64bit。
    • 另一部分是类型指针,即对象指向它类元数据的指针。虚拟机通过这个指针确定这个类是哪个类的实例。(但并不是所有的虚拟机实现都要在对象数据上保存类型指针,查找对象元数据信息不一定经过对象本身),如果对象是数组,那么对象还需要记录数组长度。
  • 实例数据
    实例数据就是各种字段内容。
  • 对齐填充(无特殊作用,相当于占位符)

对象的访问定位

JAVA程序通过栈啥上的reference数据来操作堆上的具体对象。主要有两种访问方式:

  • 使用句柄(handle)访问
    (PS:句柄是什么:https://blog.csdn.net/zwz2011
    303359/article/details/69943503)
    JAVA堆中划分出一块内存作为句柄池。

  • 使用直接指针访问

两种方式的对比

  • 句柄访问的优势是reference中存储的是稳定的句柄地址,对象被移动时,只改变句柄中的实例地址,reference不需要修改。
  • 直接访问速度快,节省了一次指针定位的开销,由于对象访问非常频繁,直接访问能明显降低执行成本。就Sun公司的JVM来说,它是以第二种方式来访问的,但就业界而言,使用句柄访问的情况也很常见。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值