BiBi - JVM -2- 对象
From:深入理解Java虚拟机
- 目录
BiBi - JVM -0- 开篇
BiBi - JVM -1- Java内存区域
BiBi - JVM -2- 对象
BiBi - JVM -3- 垃圾收集算法
BiBi - JVM -4- HotSpot JVM
BiBi - JVM -5- 垃圾回收器
BiBi - JVM -6- 回收策略
BiBi - JVM -7- Java类文件结构
BiBi - JVM -8- 类加载机制
BiBi - JVM -9- 类加载器
BiBi - JVM -10- 虚拟机字节码
BiBi - JVM -11- 编译期优化
BiBi - JVM -12- 运行期优化
BiBi - JVM -13- 并发
主要是针对HotSpot虚拟机在Java堆中对象创建、布局、访问过程的介绍。
1. 对象的创建
new一个对象时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
对象所需内存的大小在类加载完成后便可完全确定。
-
分配内存的两种方式
1)指针碰撞
Java堆中内存是绝对规整的,用过的内存和空闲的内存是分开的,中间是一个指针作为分界点的指示器。分配内存就是把这个指针向空闲区域移动一段与对象大小相等的距离。
2)空闲列表
Java堆内存并不规整,使用过的内存和空闲内存相互交错,此时虚拟机需要维护一个列表,记录哪些内存块是可用的【空闲的】。分配内存时,从列表中找到一块足够大的内存空间划分给对象实例,并更新列表上的记录。
具体采用哪种分配方式,是根据采用的垃圾收集器是否带有【压缩整理】功能决定。
-
分配内存过程的并发方案
背景:分配内存过程中需要考虑并发问题,如:给对象A分配内存时,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存。
1)同步处理
对分配内存空间的动作进行同步处理,采用CAS算法 + 失败重试的方式保证操作的原子性。
2)TLAB
内存分配的动作按照线程划分在不同的空间中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲【Thread Local Allocation Buffer,TLAB】。哪个线程需要分配内存空间,就在哪个线程的TLAB上进行分配,只有当TLAB用完并分配新的TLAB时,才需要同步锁定。虚拟机是否需要使用TLAB可以通过相应的参数来设定。
内存空间分配完后,虚拟机需要将分配的内存空间初始化为零值【默认值0或false】。执行new指令之后会接着执行<init>方法,把对象按照程序员的意愿进行初始化。
2. 对象的内存布局
HotSpot虚拟机中,对象在内存中的布局分为3块区域:对象头【Header】 + 实例数据 【Instance Data】+ 对齐填充【Padding】。
-
对象头【Header】
对象头包括两部分信息:Mark Word 和 类元数据。
Mark Word
存储对象自身运行时的数据称为【Mark Word】。包括:哈希码、GC分代年龄、锁状态标志位、线程持有的锁、偏向线程ID等。对象头信息是与对象自身定义的数据无关的额外存储,考虑到空间效率,它会根据对象的状态复用自己的存储空间。
类元数据
类型指针,即对象指向他的【类元数据】的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
-
实例数据【Instance Data】
对象真正存储的有效信息,即程序代码中所定义的各种类型的变量,【包括从父类继承下来的数据】。存储顺序为:longs/doubles、ints、shorts/chars、bytes/booleans、oops,可见相同宽度的字段总是被分配到一起,在满足这个前提下,父类中定义的变量会出现在子类之前。但默认的情况下,子类中较窄的变量也可以插入到父类变量之中。
-
对齐填充【Padding】
它不是必然存在的,也没有什么特别的含义,仅仅起占位符的作用。因为HotSpot的自动内存管理要求对象起始地址必须是8字节的整数倍,即对象的大小必须是8字节的整数倍。当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。
3. 对象的访问定位
Java程序通过栈上的reference来操作堆上的具体对象。Java虚拟机只是规定了一个reference来指向对象的引用,但并没有定义这个引用通过何种方式去定位、访问堆中的对象。目前主流的访问方式有:句柄和直接指针两种方式。
-
句柄
Java堆中会划分出一块内存作为【句柄池】,reference存储的就是句柄地址,而句柄中包含了对象实例数据和对象类型数据的地址信息。优点:在垃圾收集时,对象被移动,只会改变句柄中实例数据的地址信息,不会修改reference本身。
-
直接指针【HotSpot采用】
reference中存储的是对象实例数据地址,【此种方式Java堆对象的布局必须保存对象类型数据的地址信息】,即上述对象的内存布局中:对象头中的类型指针。优点:直接使用指针访问的最大好处就是速度快,能够节省一次指针定位的时间开销,HotSpot就是采用这种方式进行对象的访问。
对象实例数据在Java堆中,对象类型数据在方法区中。创建一个对象,最基本的两个信息:
1)该对象的数据都有什么
2)该对象是哪个类型的对象