深入理解JVM——浅析HotSpot中的对象

一、对象的创建

1、检查

当 java 虚拟机遇到一条字节码 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过,如果没有就先执行相应的类加载过程。

2、分配内存

类加载检查通过后,虚拟机将为新生对象分配内存,对象所需内存大小在类加载完成后便可确定。假设 java 内存是绝对规整的,被使用的内存放在一边,未被使用的放在另一边,中间放着一个指针作为分界点的指示器,分配内存即挪动指针(称为指针碰撞)。如果java 内存堆并不规整,虚拟机必须维护一个列表,记录哪些块是可用的,在分配时从列表中选出一块足够大的空间划分给对象实例并更新列表内容(称为空闲列表)。

3、线程安全处理

对象的创建在虚拟机是非常频繁的行为,即使仅仅修改一个指针所指的位置在并发下也并不是安全的,有两种解决办法:一是对分配空间的动作进行同步处理,实际上虚拟机采用 CAS(即比较并替换,是一种乐观锁)配上失败重试的方式保证原子性。二是把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在 Java 堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB),哪个线程要分配内存就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完后分配新的缓存区才需要同步锁定。

4、初始化0值

内存分配完后 Java 虚拟机会将分配到的内存空间(不包括对象头)都初始化为零值,若使用了 TLAB 则在分配空间时顺道进行。

5、设置对象头

6、执行init方法

此时的对象所有字段都是默认 0 值,一般来说接下来要执行< init >()方法对对象进行初始化,按照程序员的意愿对对象进行初始化,此时对象才真正创建成功。

二、对象的内存布局

(1)对象在堆内存中的布局可以划分为三部分: 对象头、实例数据和对齐填充。
(2)对象头包括两类信息:一是用于存储对象自身的运行时数据(哈希表,GC 分代年龄,锁状态等),称为 Mark Word,其被设计成有着动态定义的数据结构,以便在极小的空间存储尽量多的数据。二是类型指针,即对象指向它的类型元数据的指针,Java 虚拟机根据这个指针来确定该对象是哪个类的实例(注意并非所有对象数据上都保留类型指针)。
(3)如果对象是一个 Java 数组,那么对象头中还应该有一块用于记录数组长度的数据,因为如果数组长度不确定无法通过元数据中的信息推断出数组的大小。
(4)实例数据:真正存储有效信息的部分。
(5)对齐填充:并非必然存在的,仅仅起到占位符的作用,任何对象的大小都是 8 字节的整数倍(对象头一定是整数倍,对齐填充负责对齐实例数据的不整齐部分)。

三、对象的访问定位

(1)主流的访问方式主要有使用句柄和直接指针两种。
(2)使用句柄访问优点:reference 存储的稳定的句柄地址,即使对象被移动只会改变句柄中实例数据的指针,reference 本身不用修改。使用直接指针访问的优点:访问速度快,节省了一次指针定位的时间。(HotSpot 使用直接指针访问)。
(3)使用句柄访问:Java 堆中划分出一块区域作为句柄池,reference 存储的就是句柄地址。

(4)使用直接指针访问:reference 直接存储对象地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值