jvm中对象是怎么存储的,对象的内存布局是怎样的
对象通过new,反射,clone等方式创建完毕后被存储到堆中,那么对象是以什么样的方式存储在堆中的?
对象的内存布局
对象在堆内存的内存布局主要有三部分,即对象头,实例数据,对其填充
对象头
对象头主要包含两部分的内容,一个叫运行时元数据(mark word),一个叫类型指针
-
1.运行时元数据:
- 哈希值(hashcode):对象在堆空间中都有一个首地址值,栈空间的引用根据这个地址指向堆中的对象,这就是哈希值起的作用
- GC分代年龄:对象首先是在Eden中创建的,在经过多次GC后,如果没有被进行回收,就会在survivor中来回移动,其对应的年龄计数器会发生变化,达到阈值后会进入养老区
- 锁状态标志,在同步中判断该对象是否是锁
- 线程持有的锁
- 线程偏向ID
- 偏向时间戳
- 2.类型指针,指向元数据区的代表当前类的class对象,确定该对象所属的类型
说明:如果对象是数组,则还需要记录数组的长度
实例数据(Instance Data)
它是对象真正存储的有效信息,包括程序代码中定义的各种字段类型(包括从父类继承下来的和自己本身拥有的字段),注意这里有一些规则:相同宽度的字段总是被分配在一起,父类中定义的变量会出现在子类之前,因为父类的加载是优先于子类加载的
对齐填充
没有特殊含义,仅仅起到占位符的作用
小结
现在有一个Customer cust = new Custome()
的对象,通过上面的叙述,我们通过一张图来直观的了解下:
补充:对象访问方式
在创建好对象后,通过栈帧上的引用访问堆中的实例,这种访问方式可以分为两种方式:句柄访问和直接指针(HotSpot采用)
句柄访问:
直接指针:
和我们介绍内存布局是一样的
两种方式比较:
直接指针效率往往比句柄访问高,且不需要专门维护句柄池,内存花费小,当对象实例发生移动时,通过指针访问的引用也要发生变化,而句柄方式的reference则不需要改变