3.HotSpot虚拟机对象管理

来源:互联网 发布:梁溪淘宝 编辑:程序博客网 时间:2024/06/11 13:58
1.对象的创建
1.1类型检查
虚拟机遇到一条new指令时, 首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用, 并且检查这个符号引用代表的类是否已被加载、 解析和初始化过。 如果没有, 那必须先执行相应的类加载过程。
1.2分配内存
在类加载检查通过后, 虚拟机将为新生对象分配内存。 对象所需内存的大小在类加载完成后便可完全确定, 为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。
1.2.1 分配方式
A.指针碰撞
如果Java堆中内存是绝对规整的, 用过的内存都放在一边, 空闲的内存在另一边, 中间放一个指针作为分界点的指示器, 那所分配内存仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离, 这种分配方式称为“指针碰撞” (Bump the Pointer) 。
B.空闲列表
如果Java堆中的内存是不规整的, 已使用的内存和空闲的内存相互交错,虚拟机必须维护一个列表, 记录哪些内存块可用, 在分配的时候从列表中找到一块足够大的空间划分给对象实例, 并更新列表上的记录, 这种分配方式称为“空闲列表” (FreeList) 。
1.2.2 线程安全问题
对象创建不是线程安全的,解决这个问题有两种方案:
A.同步处理
对分配内存空间的动作进行同步处理——实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性;
B.分配缓冲
把内存分配的动作按照线程划分在不同的空间之中进行, 即每个线程在Java堆中预先分配一小块内存, 称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB) 。 哪个线程要分配内存, 就在哪个线程的TLAB上分配, 只有TLAB用完并分配新的TLAB时, 才需要同步锁定。 虚拟机是否使用TLAB, 通过-XX: +/-UseTLAB参数来设定.
1.3初始化零值
内存分配完成后, 虚拟机需要将分配到的内存空间都初始化为零值 。 这一步保证对象的实例字段在Java代码中可以不赋初始值就直接使用, 程序能访问到这些字段的数据类型所对应的零值。
1.4设置对象头
虚拟机要对对象进行必要的设置, 例如这个对象是哪个类的实例、 如何才能找到类的元数据信息、 对象的哈希码、 对象的GC分代年龄等信息。 这些信息存放在对象的对象头中。
1.5初始化
执行new指令之后会接着执行<init>方法, 把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

2.对象的内存布局
在HotSpot虚拟机中, 对象在内存中存储的布局可以分为3块区域: 对象头(Header) 、 实例数据(InstanceData) 和对齐填充(Padding) 。
2.1对象头(Header)
HotSpot虚拟机的对象头包括两部分信息:
第一部分用于存储对象自身的运行时数据, 如哈希码、 GC分代年龄、 锁状态标志、 线程持有的锁、 偏向线程ID、 偏向时间戳等, 这部分数据的长度在32位和64位的虚拟机(未开启压缩指针) 中分别为32bit和64bit, 官方称它为“Mark Word” 。

另外一部分是类型指针, 即对象指向它的类元数据的指针, 虚拟机通过这个指针来确定这个对象是哪个类的实例。
注:另外, 如果对象是一个Java数组, 那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小, 但是从数组的元数据
中却无法确定数组的大小。
2.2实例数据(InstanceData)
实例数据部分是对象真正存储的有效信息, 也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的, 还是在子类中定义的, 都需要记录起来。 HotSpot虚拟机默认的分配策略为longs/doubles、 ints、 shorts/chars、 bytes/booleans、 oops(Ordinary Object Pointers) , 相同宽度的字段总是被分配到一起。
2.3对齐填充(Padding)
对齐填充并不是必然存在的, 没有特别的含义, 它仅仅起着占位符的作用。 由HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍, 当对象实例数据部分没有对齐时, 就需要通过对齐填充来补全。

3.对象的访问定位
3.1句柄
Java堆中将会划分出一块内存来作为句柄池, reference中存储的就是对象的句柄地址, 而句柄中包含了对象实例数据与类型数据各自的具体地址信息。

3.2直接指针
Java堆对象的布局中考虑如何放置访问类型数据的相关信息, 而reference中存储的直接就是对象地址。java虚拟机Sun HotSpot, 它是使用直接指针进行对象访问的。

4.OutOfMemoryError异常实例
https://github.com/pingszi/JavaDemo.git