背景

看h2源码的过程中,由于每个Page需要持久化,便使用一个字段存放写入文件时占用的字节大小,而我对java对象占用字节大小不甚清晰,便查找资料记录如下:


java对象在堆中如何存放

java对象在堆中分为三个部分,分别是 对象头(Header),实例数据(Instance Data)和对齐填充(Padding)

对象头

分为三个字段:

  • mark
  1. 8个字节
  • klass
  1. 4个字节(32位JVM或64位JVM开启指针压缩)
  2. 8个字节(64位JVM且关闭指针压缩)
  3. 指向instanceklass
  • length
  1. 4个字节
  2. 只有数组对象会有此值,表示数组的长度

存储了Java对象hash、GC年龄、锁标记、class指针、数组长度等信息


实例数据

当前对象中的实例字段。由基本数据类型和引用类型组成的。


 

对象填充

VM要求对象大小须是8的整体数,该部分是为了让整体对象在内存中的地址空间大小达到8的整数倍而额外占用的字节数。


压缩指针

  1. 32位HotSpot VM是不支持UseCompressedOops参数的,只有64位HotSpot VM才支持。
  2. 可通过-XX:+UseCompressedOops-XX:-UseCompressedOops进行开启和关闭
  3. java7和java8默认开启

为什么用压缩指针?

  1. 4字节只能寻址到4G内存,不足;8字节可以寻址到232*4G内存,太多了
  2. 使用8bit表示oop( ordinary object pointer,一般对象指针),相较于4bit而言,会使所有应用程序运行时占用的空间大1.5倍
  3. 增加了GC开销
  4. 降低CPU缓存命中率

压缩指针原理

  1. 确保所有的对象都是8bit对齐
  2. oop指向的地址只需要保存对象的起始位置即可,没必要寻址到8bit的每一位,那么oop指向的地址的后三位全是000,可以省略,也即oop可以表示 前32位+000 ,一共35位的地址,即23*4G=32G的内存


信息是否被压缩?

哪些信息会被压缩?

  1. 对象的全局静态变量(即类属性)
  2. 对象头信息:64位平台下,原生对象头大小为16字节,压缩后为12字节
  3. 对象的引用类型:64位平台下,引用类型本身大小为8字节,压缩后为4字节
  4. 对象数组类型:64位平台下,数组类型本身大小为24字节,压缩后16字节

哪些信息不会被压缩?

  1. 指向非Heap的对象指针
  2. 局部变量、传参、返回值、NULL指针

展望

  1. 所有对象16bit对齐,压缩指针可以表示64G内存(也可以此类推)
  2. 5字节oop,可以表示1024G内存;6字节oop,可以表示65536*4G内存
  3. 变长oop



对象头大小

开启指针压缩

关闭指针压缩

是数组对象

16byte

20byte

不是数组对象

12byte

16byte