天天面向对象,四处找对象,了解对象先从对象的内存布局及访问原理开始。
对象的内存布局
对象的内存布局如下图所示
HotSpot虚拟机的对象头包括两部分信息:运行时数据和类型指针。 如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。
- 运行时数据(Mark Word)部分数据的长度在32位和64位虚拟机(未开启压缩指针)中分别为32bit和64bit。然后对象需要存储的运行时数据其实已经超过了32位、64位Bitmap结构所能记录的限度,但是对象头信息是与对象自身定义的数据无关的外存储成本,Mark
Word一般被设计为非固定的数据结构,以便存储更多的数据信息和复用自己的存储空间。- 类型指针,即指向它的类元数据的指针,用于判断对象属于哪个类的实例。(并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说,查找对象的元数据并不一定要经过对象本身)
- 实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类中继承下来的,还是在子类中定义的,都需要记录下来。各字段的分配策略为longs/doubles、ints、shorts/chars、bytes/boolean、oops(ordinary
object pointers),相同宽度的字段总是被分配到一起,便于之后取数据。父类定义的变量会出现在子类前面。- 对齐填充部分仅仅起到占位符的作用,并非必须。
对象访问原理
对象访问会涉及到Java栈、Java堆、方法区这三个内存区域。
- 使用句柄访问对象。即reference中存储的是对象句柄的地址,而句柄中包含了对象示例数据与类型数据的具体地址信息,相当于二级指针。
- 直接指针访问对象。即reference中存储的就是对象地址,相当于一级指针。
这两种访问对象的方式各有优势,使用句柄访问方式最大好处就是reference中存储的是稳定的句柄地址,在对象移动时只需要改变句柄中的实例数据指针,而reference不需要改变。使用指针访问方式最大好处就是速度快,只进行了一次指针定位,节省了时间开销,就虚拟机而言,它使用的是第二种方式(直接指针访问)。