JVM第四步——内存中的数据的使用细节

本文深入探讨Java虚拟机中对象的内存分配、布局及访问机制。覆盖类加载检查、内存划分策略、对象头详细结构、实例数据布局,以及直接指针与句柄两种访问方式。理解这些机制对于优化Java应用性能至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第二步说到虚拟机的内存布局,那么虚拟机是如何将数据存放进相对应的区域的呢,这其中的过程又是什么呢。Java堆中对象的内存分配、布局和访问。

以Java程序中最多的对象的创建为例子。(其实时书中就是以此为例子_

  • 确定那一个类
    • 遇到new指令后检查该指令的符号常量(是否被加载解析初始化),没有就先进行类的加载过程。
    • 类加载检查后为其 分配内存(所需大小在类加载阶段完成后就可确定)
  • 如何在堆中为对象划分内存
    • 指针碰撞:Java堆内存是规整的,就是将分界指示器(空闲和使用过的)的指针向空闲侧移动。
    • 空闲列表:内存不规整,维护一个可使用内存块的列表,划分是从列表中寻找一块足够使用的空间并更新空闲列表的记录。
    • 两种划分方式取决于内存是否规整,而内存的规整取决于GC是否有压缩整理功能。
  • 安全的在堆中为对象划分内存
    • JVM中对象的创建非常频繁导致并发下不是线程安全的(创建过程并非一步到位可以划分为很多步,可以被插队)。如正在为A分配空间,指针还未修改。此时对象B又使用此时的指针来分配内存。
    • 堆分配空间的动作进行同步处理:CAS家失败重试来保证更新的原子性。一直尝试分配直到分配成功。
    • 另一种从空间上来断绝修改相同区域的可能,不同线程给予不同的内存,不同线程在自己得到的内存中分配对象,这一块叫做本地线程分配缓冲。在线程共享的区域中分出线程私有的区域来实现线程安全的对象分配。
  • **分配内存后,将内存区域初始化为零值。(实例字段不赋初始值是不能使用的)(不包括对象头)**实例字段是通过对象去引用的。
  • 虚拟机要对对象进行必要的设置,主要是对象头的设置。对象头如下
    • 对象头主要分为两部分
      • Mark Word运行时数据(告知虚拟机如何来处理使用对象)这是额外开销与对象本身数据无关
        • 运行时数据是一个非固定的可复用的数据结构空间,所以可以在小空间存储尽量多的内容。具体有如下
        • 对象的哈希码:用来判断是否同一个对象,为对象的特征
        • GC分代年龄:通过分代年龄,虚拟机才知道如何去管理该片数据区域(后文的年龄限制进入老年区)。
        • 指向锁的指针:锁优化中的轻量级锁,通过状态的改变来达到资源唯一使用性。
        • 指向重量级锁的指针
        • 偏向锁偏向的线程ID
        • 等等都是方便虚拟机来使用对象,与对象本身所存储的数据无关。
      • 类型指针 -确定对象属于那个类的实例的指向类元数据的指针
      • 如果是数组对象那么对象头中有记录数组长度的数据,普通Java对象可以在类元数据中确定对象的大小。
  • 至此在虚拟机看来对象已经生产出来了,只不过所有的变量都是零值不是程序员期望的值(init方法还未执行。),init方法执行后真正可用的对象才生产出来。

对象的内存布局 --对象在堆中到底是如何存放的
针对虚拟机的不同会有不同,以HotSpot为例。
对象在内存中存储的数据主要分为3部分:

  • 对象头:在前面虚拟机对对象的设置时有介绍对象头。
  • 实例数据
    • 此部分存储的是真正有效的数据(程序中定义的各种字段内容)
    • 父类继承,子类定义的都在这里
    • 相同宽度的字段分配在一起,父类中的变量在子类之前。
  • 对其填充
    • 仅仅有占位符的作用,虚拟机自动内存管理系统要求地址必须是8字节的整数倍。

定位对象 --如何来访问堆中的对象

  • Java程序通过栈上的reference数据来操作堆上的具体对象。reference数据能做到两点
    • 通过该引用可以访问该对象在堆中存放数据的起始地址。
    • 通过该引用g可以对象所属的类的类型信息(在方法区,类加载机制将类加载进方法区中)。
  • reference只是一个指向对象的引用,并不是如何去定位、访问堆中的对象数据。
  • 对象的访问方式取决于虚拟机实现,主流方式有如下两种:
    • 句柄
      • Java堆中划分中一块区域作为句柄池,reference存储的就是对象的句柄地址。
      • 句柄存储有:
        •   对象的实例数据地址(堆中,对象内存布局中存储有效数据的实例数据)
          
        •   类型数据(方法区中)
          
      • 好处就是reference存储的一直是句柄地址,对象移动时只需改变句柄中的实例地址。
    • 直接指针
      • 存储的是对象的直接地址但是需要对象自己存储访问类型数据的地址(方法区中)。
      • 好处就是速度快,一部到位。少一步指针定位(到句柄)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值