Java核心02-HotSpot 虚拟机对象

对象的创建过程

        

        1.类加载检查

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

        2.分配内存

  • 对象所需的内存大小在类加载完成后便可知,为对象分配空间的任务等同于把一块确定大小的内存,从Java堆中划分出来。
  • 分配方式有”指针碰撞“和”空闲列表“两种选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

       

  • 虚拟机采用以下两种方式来保证线程安全性:
    • 指针碰撞(CAS+失败重试):CAS是乐观锁的一种实现方式。冲突失败就重试,直到成功为止。保证更新操作的原子性。
    • 空闲列表(TLAB):为每个线程预先在Eden区分配一块内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中的剩余内存,或TLAB的内存已用尽时,再采用上述的CAS+重试 进行内存分配。

        3.初始化零值

  • 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头)。从而保证了对象的实例字段在Java代码中可不赋初始值就可直接使用,程序能访问到这些字段的数据类型所对应的零值。

        4.设置对象头

  • 初始化零值完成后,虚拟机要对对象进行必要的设置。比如这个对象是哪个实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息,存放在对象头中。另外根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

        5.初始化-执行init方法

  • 以上工作完成后,从虚拟机角度来看,一个新的对象已经产生了。但从Java程序视角来说,对象创建才刚开始。<init>方法还未执行,所有的字段都还是为零。所以一般来说,执行new 指令后会接着执行<init>方法,把对象按照程序设计进行初始化,结束后才算完全创建完成。
对象的内存布局

        1.打印对象的内存占用大小:

//open jdl 插件
ClassLayout.parseInstance(o).toPrintable();

        2.在HotSpot 虚拟机中,对象在内存中布局分为3块区域:对象头、实例数据和对齐填充。

        3.对象头包括2部分信息:

  • 第一部分是用于存储对象自身的运行时数据(对象的哈希码、GC 分代年龄、锁状态标志等)Mark Word(标记字段)
  • 第二部分是类型指针(Klass Pointer)。即对象指向它的类元数据的指针,虚拟机通过这个指针来确定该对象是哪个类的实例。
  • 64位虚拟机普通对象的对象头数据结构:

        4.实例数据部分是对象真正存储的有效信息,也是程序中所定义的各种类型的字段内容。

        5.对齐填充部分仅仅起占位作用。因为HotSpot虚拟机的自动内存管理系统要求,对象起始地址必须是8 字节(64位)的整数倍。

Object   Header(128 bit)

Mark Word(64   bit)

Klass Word(64 bit)

锁状态

56 bit

4 bit

1 bit

2 bit

54 bit

2 bit

1 bit

是否偏向锁

锁标志位

对象类型指针

无锁

unused25 bit

hashCode

31 bit

unused

1 bit

对象分代年龄

4 bit

0

01

指向元数据对象的指针

偏向锁

线程Id

54 bit

Epoch

2 bit

unused1 bit

对象分代年龄

4 bit

1

01

指向元数据对象的指针

轻量级锁

指向栈中锁记录的指针

00

指向元数据对象的指针

重量级锁

指向互斥量(重量级锁)的指针

10

指向元数据对象的指针

GC标记

11

指向元数据对象的指针

对象的访问定位
  1. 创建对象的目的是使用对象。Java程序通过栈上的reference(引用)数据来操作堆上具体对象。
  2. 对象的访问方式由虚拟机实现而定。有使用句柄和直接指针两种。
  • 使用句柄Java堆中将会划分出一块内存来作为句柄池,栈上的reference中存储的就是对象的句柄地址。而句柄中包含了对象实例数据与类型数据各自的具体地址信息。最大好处是reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference 本身不需要修改。

          

  • 直接指针:Java堆对象的布局中,就必须考虑如何放置访问类型数据的相关信息。而reference中存储的直接就是对象的地址。最大好处时速度快,节省了一次指针定位的时间开销。

        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值