1、对象的内存分配由两种形式
- 指针碰撞
- 空闲列表
两种类型适用于不同场景(不同垃圾回收算法)
碰撞指针适用于堆内存规整
空闲列表适用于已分配内存与空闲内存相互交错的场景下
具体使用哪种对象分配内存取决于垃圾回收算法,例如当使用Serial、ParNew等带有压缩整理功能的收集器时,系统采用分配的方法时指针碰撞;而当使用CMS这种基于标记清除算法的收集器时,理论上来说是用空闲列表,但在CMS的实现里面,为了更快的分配内存,设计了linear Allocation Buffer的分配缓冲区,通过空闲列表拿到一大块分配空闲区,在内部仍然可以使用指针碰撞。
2、对象的内存布局
在HotSPot虚拟机中,对象的内存布局可以分为三部分
- 对象头
- 实例数据
- 对齐补充
对象头
对象头中包含两类信息。第一类是用于存储对象自身的运行时数据,第二类是类型指针。
运行时数据包括哈希码(HashCode)、GC分代年龄、锁状态、线程持有的锁、偏向线程ID、偏向时间戳等。这部分数据在32bit、64bit BitMap结构中已经超出所能记录的最大程度,考虑到虚拟机的空间效率,Mark Word被设计成动态定义的数据结构,以便在极小的空间内存储尽量多的数据,根据对象的状态复用自己的存储空间。
类型指针可以通过这类信息来确定是哪个类的实例对象。
实例数据
实例数据存储这当前实例对象真正有效的信息。
对齐补充
这个仅仅用作占位符的作用。在HotSPot虚拟机中自动内存管理要求对象的其实位置必须是8字节的整数倍。
3、对象的定位
对象的定位一共有两种形式
- 句柄
- 直接指针
使用句柄定位的形式,Java堆中会分配出一块内存空间用作句柄池,栈中的reference数据存储的就是句柄池中的稳定句柄,当堆中的对象发生位置移动时,只需改变句柄中的数据,而reference数据不用改变。
使用直接指针的形式,栈中的reference数据存储的是对象地址,这样的话减少了间接性访问,速度更快,但是JAVA堆就需要考虑如何放置类型数据的相关信息。