【Java】深入理解JVM学习笔记(二) —— 对象

【问题一】程序通过new一个普通对象的时候,虚拟机进行了哪些操作?
解答: ①首先检查这个指令的参数是否能在常量池中定位到这个类引用
②并且检查这个符号引用代表的类是否被加载、解析、初始化过③若没有加载,必须执行相应类的类加载过程④虚拟机为新生对象分配内存(对象所需内存大小,在类加载时间可以完全确定)

④ 分配内存

为对象分配大小相当于把一块确定大小的内存从Java堆中划分出来。假设Java内存是规整的,所有用过的内存放在一边,所有空闲的内存放在一边,中间放着一个指针作为分界点的指示器。对象大小就是指针向空闲内存移动的距离,这种分配方式叫做指针碰撞。假设内存是不规整的,已使用的空间和空闲空间相互交错,虚拟机须维护一个列表,存放可用内存空间,在分配的时候根据列表找到一块足够大的空间给对象,并更新列表上的记录,这种分配被称为“空闲列表”
总结:选择哪种分配方式是通过Java内存是否规整决定,而Java是否规整又由采集的垃圾收集器(GC)是否带有压缩整理功能决定

Compact收集器采用指针碰撞方式,而Mark-Slweep采用空闲列表


分配对象可能是线程不安全的,虚拟机采用2种方式保证更新操作的原子性

  • CAS+失败重试方式
  • 每个线程预分配一小块内存(本地线程分配缓冲区TLAB)。哪个线程需要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的TLAB,才同步锁定

使用TLAB

-XX:+/-UseTLAB

⑤ 初始化

虚拟机将分配到的内存空间初始化为0.如果使用TLAB,可将这一操作提前到分配TLAB时进行。
保证Java实例对象字段,不需要初始化,就可以直接使用。

⑥ 必要设置

虚拟机对对象进行必要的设置。例如: 对象属于哪个类实例、如何找到类的元数据信息、对象哈希码、对象GC分代年龄等设置

以上完成后。对于虚拟机来说,一个对象已经产生了,但是从Java的角度看,一个对象的创建刚刚开始——init()还未执行,所有字段还是0.

注意:当一个对象初始化之后,一个真正可用的对象才算生成。


对象的内存布局

对象在内存中的布局包含:对象头、实例数据、对齐填充
HotSpot虚拟机对象头包括:存储自身对象运行数据和类型指针

  • 存储自身对象
    • 哈希码、GC分代年龄、锁标志、线程持有锁、偏向线程ID、偏向时间戳等
  • 类型指针
    • 通过这个指针确定这个对象属于哪个类实例。但是不是所有虚拟机都必须在对象数据类型保留类型指针,所以查找对象的元数据信息不一定要经过类本身。如果是一个数组,还需要记录长度数据。

实例数据字段分配策略顺序:longs/doubles -> int -> short/chars -> byte/boolean -> oops

  1. 相同宽度的字段总是分配在一起
  2. 父类定义的变量总是在子类之前
  3. 对其填充仅仅起占位符的作用

对象访问定位

通过栈上的reference数据操作堆上数据
reference只提供了一个指向对象的引用,访问定位主流方式有使用句柄和直接指针两种方式实现

访问方式

  • 使用句柄

    • Java堆划分一块内存作为句柄池,reference存储对象句柄地址。而句柄中包含对象实例数据和类型数据各自的地址信息
  • 使用直接指针

    • reference存储的是对象地址

句柄访问方式
句柄访问方式

直接指针访问方式
直接指针访问方式

句柄访问优点:在对象移动时,改变的只是句柄中的实例数据本身,而reference本身并不改变

直接指针的优点:访问速度快,节省了指针定位的开销

  • -
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值