Skip to content

禁止指令重排序

对象的内存布局

在 hotspot 虚拟机中,对象在堆内存的内存分部: 对象的头信息(hander)、实例信息(Instance Data)、对齐填充 (Padding)。三部分组成。

头信息: 主要分为两部分

  • MarkWord 对象自身的运行时数据 (没有开启指针压缩时所占的内存: 32/64 比特,具体针对为 32 位还是 64 为的操作系统)
    • 哈希码值 : Object:: hashCode ();
    • GC 分代年龄
    • 锁状态标志
    • 线程持有的锁
    • 锁偏向 ID, 偏向时间戳
  • 类型指针 (实例指向类的指针)
    • 对象指向他的类型元数据的指针。(通过这个指针,来确定当前这个对象是哪个类的实例)
    • 如果这个对象是个数组,那么这里还会有一个记录,记录着这个对象的数组长度
      • 为什么要记录长度
      • 因为 JVM 需要通过 Java 类的元数据信息,确定 Java 对象的大小,进而去做内存分配。

实例信息: 对象真正存储的有效信息,对象的内部信息,成员信息等

  • 在程序中定义的各个类型的字段内容,无论是父类继承的,还是子类自己定义的。

对齐填充: 这部分数据不是必须存在的,也没有什么特别的意义,本质上就是个占位符。背景是因为 hotspot 的自动内存管理系统要求,对象的起止内存地址必须是 8 字节的倍数。

对象的访问定位

通过了解运行时数据区和类加载机制,加上了解了对象的内存模型,后面需要知道对象的访问定位是怎么做的。

创建对象的目的是为了后续的使用,Java 通过 Refresh 类型来指向句柄或堆内存的实例指针。

Refresh 是什么: 是一种引用类型,它可以指向对象的引用。通过两种方式

  • 句柄引用: Refresh 指向堆内存的句柄池,由句柄池指向堆内存的对象实例。
    • 特点: 在 JVM 进行垃圾回收时,句柄本身不会改变,只会修改句柄所指向的对象实例引用
  • 直接引用: 直接指向堆内存的对象实例
    • 特点: 访问速度更高效,相对句柄引用节省了一次指针定位的时间开销

OOM 异常 (OutOfMemoryError)

在 JVM 虚拟机中,除了应用计数器以外,其他的任何地方都可能会 oom,

下载 dump 文件

通过参数-XX:+HeapDumpOnOutOf-MemoryError 可以让虚拟机发生 oom 的时候产生出一个 dump 文件, 后续可以通过 dump 文件进一步分析 oom 问题

waitingresult.com