首页 > 技术文章 > JVM之对象创建、对象内存布局、对象访问定位

sheung 2021-03-17 15:40 原文

对象创建

  • 类加载过后可以直接确定一个对象的大小
  • 对象栈上分配是通过逃逸分析判定、标量替换实现的,即把不存在逃逸的对象拆散,将成员变量恢复到基本类型,直接在栈上创建若干个成员变量
  • 选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理 功能决定。因此,在使用Serial、ParNew等带Compact过程的收集器时,系统采用的分配算法是指针碰撞,而使用 CMS这种基于Mark-Sweep算法的收集器时,通常采用空闲列表
  • 空间并发分配解决方案
    • TLAB,线程私有空间分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定
    • CAS+失败重试保证更新操作的原子性

对象内存布局

内存布局

image

  • Klass Word(类指针):存储对象的类型指针,该指针指向它的类元数据。从JDK 1.6 update14开始,64位的JVM正式支持了-XX:+UseCompressedOops(默认开启),可以压缩指针,起到节约内存占用的作用。oop(ordinary object pointer)即普通对象指针,下列指针将压缩至32位:

    • 每个Class的属性指针(静态成员变量)
    • 每个对象的属性指针(对象变量)
    • 普通对象数组的每个元素指针
  • 指针压缩:

    • 如果GC堆大小在4G以下,直接砍掉高32位,避免了编码解码过程(偏移量除以/乘以8)
    • 如果GC堆大小在4G以上32G以下,则启用-XX:+UseCompressedOops命令
    • 如果GC堆大小大于32G,压指失效,使用原来的64位
  • -XX:+UseCompressedClassPointers

    • Java8使用Metaspace存储元数据,开启后类元信息中的指针也用32bit的Compressed版本,即Klass Word
    • 依赖-XX:+UseCompressedOops
  • 数组长度64位JVM的情况下也被压缩至32位

  • 对齐字节:HotSpot VM的自动内存管理要求对象大小必须是8字节的整数倍,不足时需要对齐填充来补全

对象头

image

  • 32位虚拟机占用32个字节,不同状态下各个比特位区间大小有变化

  • biased_lock:偏向锁标记,为1时表示对象启用偏向锁

  • age:默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,所以最大值为15

  • identity_hashcode

    • 采用延迟加载技术,只有在需要时使用System.identityHashCode(Object x)计算后写到该对象头中
    • 偏向锁没有存储HashCode的地方,偏向锁期间调用System.identityHashCode(x)会造成锁升级
    • 轻量级锁和重量级锁所指向的lock record或monitor都有存储HashCode的空间
    • 用户自定义hashCode()方法所返回的值不存在Mark Word中,只针对identity hash code
  • thread:持有偏向锁的线程ID

  • epoch:偏向锁的时间戳

  • ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针

  • ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针

ClassLayout

// 引入依赖
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.11</version>
</dependency>
        
// 查看对象布局信息        
ClassLayout layout = ClassLayout.parseInstance(new A());
System.out.println(layout.toPrintable());

可通过ClassLayout查看对象布局信息,即对象占用空间情况

对象访问定位

句柄访问对象 直接指针访问对象
img img
  • 句柄访问:java堆中会划分一块内存作为句柄池,线程栈中引用中存储的就是对象句柄地址。好处在于对象句柄地址固定,对象移动(垃圾回收时移动对象非常普遍)时仅改变句柄中的实例数据指针
  • 直接指针访问:线程栈中引用中存储的直接就是对象地址,好处在于速度更快(Sun HotSpot使用此种)

推荐阅读