七、JVM中的对象

54 阅读3分钟

JVM中的对象

对象创建过程

  1. 判断是否已经加载了对应的类,如果没有,进行类加载
  2. 类加载,先将字节码文件加载到内存
  3. 校验字节码文件内容是否正确
  4. 给静态变量赋默认值
  5. 给常量池中的符号引用替换成地址引用
  6. 给静态变量赋初始值,并且执行静态代码块
  7. 在堆区分配内存空间
  8. 给成员变量赋默认值
  9. 调用构造方法,给成员变量赋初始值

对象在内存中的存储布局

普通对象

  • 对象头:占用8个字节
  • 指向类对象的指针:如果开启-XX:+UseCompressedClassPointers参数为4字节,不开启为8字节
  • 实例数据
  • 对齐字节数:为了让对象大小为8的倍数

数组对象

  • 对象头:占用8个字节
  • 指向类对象的指针:如果开启-XX:+UseCompressedClassPointers参数为4字节,不开启为8字节
  • 数组长度:占用4个字节
  • 实例数据
  • 对齐字节数:为了让对象大小为8的倍数

对于普通对象和数组对象,对象头的内容固定占8个字节。

指向类对象的指针大小受UseCompressedClassPointers参数的影响,如果开启,指针大小压缩到4个字节,如果关闭,指针大小是8个字节。

实例数据的大小和成员属性的类型有关。一个对象的成员属性的类型有两种,一种是引用类型,一种是非引用类型。对于引用类型的属性大小,其受UseCompressedOops参数影响,如果开启,引用类型的属性指针大小是4个字节,如果关闭,大小是8个字节。非引用类型的属性,其大小就是类型占用的固定字节数,例如int类型就占4个字节。

数组对象比普通对象多一个数组长度,固定占4个字节。

最后一个对齐字节数,为了让对象大小为8的倍数进行填充的。

对象头信息

对象头中的信息是随着对象锁的状态而变化的。

image.png

  • 无锁:25位-hash值、4位-GC年龄、1位-是否偏向锁(0)、2位-锁状态(01)
  • 偏向锁:23位-线程ID、2位-空、4位-GC年龄、1位-是否偏向锁(1)、2位-锁状态(01)
  • 轻量锁:30位-锁信息、2位-锁状态(00)
  • 重量锁:30位-锁信息、2位-锁状态(10)
  • GC:30位-空、2位-锁状态(11)

注意

当对象在无锁状态下,对象头中存储了hashcode值后(在调用hashcode方法后,hashcode值会存在对象头中),如果对其加锁,它是无法变成偏向锁的,因为偏向锁的对象头前25位要存储线程ID,但是位置已经被hashcode值占据了。

当对象加了轻量级锁、重量级锁时,对象头中hashcode值、分代年龄等信息迁移到了栈空间中,对象头中只存储了指向那块空间的指针。