禁止指令重排序
对象的内存布局
在 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 问题