首页 > 其他 > 详细

JVM之HotSpot中堆里的对象

时间:2019-06-25 18:40:50      阅读:96      评论:0      收藏:0      [点我收藏+]

HotSpot是一种JVM的实现,它也是目前适用范围最广的Java虚拟机,拥有准确式内存管理和热点代码探测技术等优势。所谓准确式内存管理(Exact Memory Management),就是指虚拟机能够准确判断内存中存放的数据类型,需要了解的话可以进一步去学习。本次主要记录HotSpot中堆内存里对象创建,内存分配,内存布局以及内存访问方式。

一、对象创建的过程

我们平时创建对象时用的最多的就是new关键字,那么从new指令之后,在虚拟机中又是如何创建对象的呢?

  • 检查参数:首先JVM会检测到new指令,然后检查new指令的参数(类名)是否定位到类的符号引用,并检查该类是否已经被加载,解析和初始化过。
  • 为对象分配内存:类的符号引用没有问题,加载也已经完成,则开始为对象分配内存。这里需要注意,每个对象的内存在类加载之后就已经确定了,所以划分的内存大小是已知的。
  • 对象的内存初始化:将对象内存中的实例数据部分全部初始化为零值。
  • 对象的设置:设置对象头,存放一些必要的信息(对象所属类、类的元数据指针、对象哈希码、GC分代年龄等)
  • 至此虚拟机的工作完成,对象在堆内存中产生。之后虚拟机会执行Java程序中的<init>方法,根据用户的需求进行对象的初始化工作,完成用户初始化之后,对象才算真正可用。

ps:对象创建过程中有两点比较重要的部分:

  1. 在为对象进行内存分配的时候,不同堆内存的特点有不同的做法。对于规整的堆内存(已经使用过和未使用的内存界限分明),可以通过指针碰撞来分配内存,具体实现就是将指针后移一段距离;对于不规整的堆内存,可以使用空闲列表的方式进行内存分配,即将可用内存记录在表中,分配对象内存时首先在空闲列表中查找足够大的内存空间,然后再进行分配。
  2. 堆内存是线程共享的,在内存分配过程中也可能会出现并发问题。如A对象的内存还为分配完,B对象也分配与之相同的内存,这样会出现并发问题。处理方法有两种,一是采用CAS+失败重试保证内存分配的原子性,二是使用本地线程分配缓冲(TLAB),在堆内存中为每个线程预先分配一小部分内存,该线程的内存用完了就再分配TLAB给它,TLAB的分配需要同步锁定。

二、对象的内存布局

前面提到了在对象的内存初始化以及设置过程中,我们分别设置对象的实例数据和对象头。那么对象的内存布局分为三部分:

  • 对象头:对象头包含对象的一些基本信息,主要分为两部分:
    • 自身运行时数据:如哈希码、分代年龄、锁的状态等。这部分数据的长度是32bit/64bit,依据虚拟机位数确定。
    • 类型指针:存放类的元数据指针,可以通过这个数据确定对象所属的类。
  • 实例数据:对象的实例数据就是我们经常用到的各种字段内容,它包含继承的字段和自己定义的字段。
  • 对齐填充:对齐填充只是为了让对象的内存大小保证是8字节的整数倍,没有实际意义。

三、对象的访问方式

对象构建完成之后,我们需要通过引用变量来访问对象,操作对象。对象的访问方式分为两种:

  • 通过句柄访问:通过句柄方式访问时候,我们需要在堆中开辟一部分空间作为句柄池,句柄池中的句柄存放每个对象的地址(堆)以及其类型数据(方法区)的指针。栈上的引用数据指向对象的句柄地址,然后通过句柄再访问对象实例及其类型数据。优点是栈中存储的句柄地址稳定,对象被收集之后只需要修改句柄,无需修改栈,缺点是堆内存中要分一部分空间作为句柄池。

技术分享图片

 

  • 直接访问:直接访问的方式就是在对象的内存布局中放置类型数据相关信息,栈上的引用数据直接指向对象。优点是直接访问对象速度更快,节省指针开销。

技术分享图片

 

JVM之HotSpot中堆里的对象

原文:https://www.cnblogs.com/zhengshuangxi/p/11084372.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!