【jvm】一文带你搞懂java对象在内存中具体的结构(八股必背)

        当一个 Java 对象被创建时,它会被分配在堆内存中

        java对象在内存中的结构可以分为三个重要的部分——对象头(Header)、实例数据(Instance Data)、对其填充(Padding)

对象头(Header)

        对象头主要是由两部分或三部分组成,对象头是用来存储与对象本身相关信息的部分

        两部分:MarkWord、类指针

        数组(三部分):MarkWord、类指针、数组长度

Markword

        Markword中又包含锁信息对象hashcodeGC标志

锁信息

锁状态:Markword 中的一些位用来表示锁状态,以指示对象是否被锁定。
  • 未锁定状态:当对象未被任何线程锁定时,Markword 中的锁状态位将被设置为未锁定状态。
  • 偏向锁状态:当对象处于偏向锁状态时,Markword 中的锁状态位将被设置为偏向锁状态,并且会记录持有该锁的线程ID和一些额外的信息。
  • 轻量级锁状态:当对象处于轻量级锁状态时,Markword 中的锁状态位将被设置为轻量级锁状态,并且会记录锁记录的地址。
  • 重量级锁状态:当对象处于重量级锁状态时,Markword 中的锁状态位将被设置为重量级锁状态,并且会记录锁的指针等相关信息。

锁标志:除了锁状态外,Markword 中的其他位可能还会用于表示锁相关的标志信息,如是否启用偏向锁、是否启用自旋等。

具体判断一个对象是否被锁定的逻辑如下:

  • 如果 Markword 中的锁状态位表示未锁定状态,那么对象没有被其他线程锁定,可以被当前线程获取锁。
  • 如果 Markword 中的锁状态位表示偏向锁状态,并且记录的持有该锁的线程ID与当前线程的ID一致,那么当前线程可以获取锁。
  • 如果 Markword 中的锁状态位表示轻量级锁状态,表示锁已经被获取。当前线程会进入自旋等待,尝试在短时间内重新获取锁。
  • 如果 Markword 中的锁状态位表示重量级锁状态,表示锁已被其他线程获取,当前线程会被阻塞,等待锁释放。

对象的哈希码

        有些 JVM 实现会将对象的哈希码存储在 Markword 中。这样做可以在计算哈希码时避免额外的开销。

GC标志

        JVM 实现使用 Markword 中的位来标记对象的垃圾回收状态。例如,用一个位表示对象是否被标记为垃圾,或者用另一个位表示是否经过了写屏障等。

  1. 垃圾状态标记: 一个常见的 GC 标志是用于标记对象是否被标记为垃圾。即当垃圾回收器确定某个对象不再被引用时,将该对象的 GC 标志置为垃圾状态。这样,在后续的垃圾回收过程中,回收器会优先回收那些被标记为垃圾的对象,以释放它们占用的内存空间。

  2. 写屏障标记: 另一个常见的 GC 标志是用于标记对象在写操作(如字段赋值)之后是否经过了写屏障。写屏障是一种技术,用于跟踪对象之间的引用关系,以便在垃圾回收过程中正确地处理对象的可达性。当对象经过写屏障时,相关的引用关系会被垃圾回收器捕获并更新,以反映最新的引用状态。

类指针KlassPointer

        类指针(Class Pointer)是 Java 对象头中的一个重要组成部分,用于指向对象所属的类或元数据。它在内存中保存着对象的类型信息,使得 JVM 能够在运行时动态地确定对象的类以及可以调用哪些方法。

        在 Java 中,每个对象都有一个 Class 对象与之相关联,该 Class 对象描述了该对象的类型信息,包括字段、方法、父类等。在对象头中,类指针就是指向这个 Class 对象的引用。

类指针的作用主要有以下几个方面:

  1. 对象的类型识别: 类指针使得 JVM 能够在运行时准确地确定对象的类。当需要调用对象的方法或访问其字段时,通过类指针可以快速定位并获取相关的类信息,以确保正确的操作和类型安全。

  2. 方法的动态分派: Java 的方法调用是基于动态分派的,即在运行时根据实际对象的类型来确定调用哪个版本的方法。类指针提供了类型信息,使得 JVM 能够在执行方法调用时进行动态分派,找到正确的方法实现。

  3. 类的元数据访问: 类指针也允许 JVM 在运行时获取类的元数据信息,例如父类、接口、注解、泛型等。这些元数据对于实现反射、动态代理、序列化等功能非常重要。

        需要注意的是,类指针只是引用了 Class 对象,并不直接存储类的完整信息。Class 对象本身被存储在方法区(Method Area)中,其中包含了类的结构、常量池、静态变量等内容。

        由于类指针的存在,Java 可以实现许多面向对象的特性,如继承、多态等。它在 JVM 运行期间起着重要的作用,帮助虚拟机正确地处理对象的类型和方法调用。

实例数据

        实例数据部分是对象真正存储的有效信息,也就是我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的都需要记录下来。 这部分的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在Java源码中定义顺序的影响。

        HotSpot虚拟机 默认的分配策略为longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers),从分配策略中可以看出,相同宽度的字段总是被分配到一起。在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。如果 CompactFields参数值为true(默认为true),那子类之中较窄的变量也可能会插入到父类变量的空隙之中。

对齐填充

        第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是8字节的整数倍。对象头正好是8字节的倍数(1倍或者2倍),因此当对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值