对象的内存布局
在Hotspot虚拟机中,对象在内存中的布局可以分为三块区域: 对象头(Header)、实例数据(Instance Data)和对其填充(Padding)。
对象的内存布局
- 对象头包含两部分信息: 第一部分用于存储对象自身的运行时数据,如哈希吗(hashCode),GC分代年龄,锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据在32位和64位的虚拟机中分别为32bit和64bit,官方成为Mark word,对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定大小的数据结构,以便在有效的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。
- 对象头的另一部分是类型指针,用来确定对象是哪个类的实例对象。并不是所有的对象都会保留类型指针,如果对象是一个Java数组的话,还必须在对象头中记录数组长度,因为虚拟机可以通过普通的Java对象的元数据来确定Java对象的大小,但是从数组的元数据中却无法确定数组的大小。
- 实例数据部分,是对象存储的真正有效信息。也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录下来。
- 对齐填充。并不是必然存在,也没有特别的含义。仅仅起到了占位符的作用。
32位的Hotspot虚拟机对象头Mark Word
存储内容 | 标志位 | 状态 |
---|---|---|
对象哈希吗、对象分代年龄 | 01 | 未锁定 |
指向锁记录的指针 | 00 | 轻量级锁定 |
指向重量级锁的指针 | 10 | 膨胀(重量级锁定) |
空,不需要记录信息 | 11 | GC标记 |
偏向线程ID、偏向时间戳、对象分代年龄 | 01 | 可偏向 |
对象的访问定位
Java程序需要通过栈上的reference数据类操作堆上的具体对象。由于reference类型在Java虚拟机规范中只定义了一个对象的引用,并没有定义这个引用应该通过何种方式去定位、访问堆中的对象的具体位置,所以对象的访问方式取决于虚拟机实现而定的
使用句柄方式:堆中将会划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含对象的实例数据和类型数据各自的具体地址
句柄方式访问对象.png图片来自于《深入理解Java虚拟机》
直接指针方式:Java堆中对象的布局就必须考虑如何放置访问类型数据的相关信息,而reference中存储的就是对象的地址
直接指针方式访问对象.png图片来自于《深入理解Java虚拟机》
- 使用句柄的方式有点:reference中存储的是稳定的句柄地址,在对象被移动(GC)时只会改变句柄中的实例数据指针,而reference本身不需要修改。直接指针的的优点是:访问定位对象速度快,因为他节省了一次指针定位的开销。
写在最后
- 慢慢的让自己全身心的投入一件事情,从深度开始-到广度-再深
参考资料
周志明《深入理解Java虚拟机》
网友评论