JVM中的对象
对象创建过程
- 判断是否已经加载了对应的类,如果没有,进行类加载
- 类加载,先将字节码文件加载到内存
- 校验字节码文件内容是否正确
- 给静态变量赋默认值
- 给常量池中的符号引用替换成地址引用
- 给静态变量赋初始值,并且执行静态代码块
- 在堆区分配内存空间
- 给成员变量赋默认值
- 调用构造方法,给成员变量赋初始值
对象在内存中的存储布局
普通对象
- 对象头:占用8个字节
- 指向类对象的指针:如果开启-XX:+UseCompressedClassPointers参数为4字节,不开启为8字节
- 实例数据
- 对齐字节数:为了让对象大小为8的倍数
数组对象
- 对象头:占用8个字节
- 指向类对象的指针:如果开启-XX:+UseCompressedClassPointers参数为4字节,不开启为8字节
- 数组长度:占用4个字节
- 实例数据
- 对齐字节数:为了让对象大小为8的倍数
对于普通对象和数组对象,对象头的内容固定占8个字节。
指向类对象的指针大小受UseCompressedClassPointers参数的影响,如果开启,指针大小压缩到4个字节,如果关闭,指针大小是8个字节。
实例数据的大小和成员属性的类型有关。一个对象的成员属性的类型有两种,一种是引用类型,一种是非引用类型。对于引用类型的属性大小,其受UseCompressedOops参数影响,如果开启,引用类型的属性指针大小是4个字节,如果关闭,大小是8个字节。非引用类型的属性,其大小就是类型占用的固定字节数,例如int类型就占4个字节。
数组对象比普通对象多一个数组长度,固定占4个字节。
最后一个对齐字节数,为了让对象大小为8的倍数进行填充的。
对象头信息
对象头中的信息是随着对象锁的状态而变化的。
- 无锁: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值、分代年龄等信息迁移到了栈空间中,对象头中只存储了指向那块空间的指针。