JUC多并发编程 对象内存布局

文章详细介绍了Hotspot虚拟机中对象在堆内存的布局,包括对象头(MarkWord用于存储对象哈希码、年龄和锁状态等),类元数据(指向类元信息的指针),实例数据(存储属性信息)以及对齐填充(确保8字节对齐)。此外,还提到了压缩指针的使用情况及其对内存布局的影响。

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

对象的内存布局简介

在 Hotspot 虚拟机里,对象在堆内存中存在布局可划分为三个部分: 对象头(Header), 实例数据(Instance Data) 和对齐填充(Padding 保证8字节位数)

对象头

  • 对象标记 MarkWord, 在64位操作系统中, Mark Word 占8个字节, 类型占 8个字节,一共 16个字节
  • GC 年龄采用 4 位 bit 存储, 最大为 15, MaxTenuringThreshold = 15

Mark Word 标记:

存储内容标志位状态
对象哈希码、对象分代年龄01未锁定
指向锁记录的指针00轻量级锁定
指向重量级锁的指针10膨胀(重量级锁)
空、不需要记录信息11GC 标记
偏向线程ID、偏向时间戳、对象分代年龄01可偏向

Mark Word的存储结构:

锁状态25bit31bit1bit4bit1bit2bit
cms_free分代年龄偏向锁锁标志位
无锁unusedhashCode001
偏向锁ThreadID(54 bit) Epoch(2bit)101
轻量级锁指向栈中的锁的记录的指针00
重量级锁指向重量级锁的指针10
GC 标志11

类元数据(类型指针):

  • 对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

实例数据

  • 存放类的属性(Field)数据信息,包括父类的属性信息

对齐填充

  • 虚拟机要求对象起始地址必须是8的字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐这部分内存按 8 字节补充对齐

实例代码:

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;

public class JOLDemo {
    public static void main(String[] args){

//        // VM 的细节详细情况
//        System.out.println(VM.current().details());
//        // 所有的对象分配的字节都是 8 的整数倍
//        System.out.println(VM.current().objectAlignment());

        // OFFSET 偏移量,也就是这个字段位置所占用的 byte 数
        // SIZE 后面类型的字节大小
        // TYPE 是 Class 中定义的类型
        // DESCRIPTION 类型的描述
        // TYPE 在内存中的值
        // 前两行是 MarkWord 8 字节, 第三行是 类型指针 4 字节
        // 对象头情景1
        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());

        // 对象头 情景2
        Customer c1 = new Customer();
        System.out.println(ClassLayout.parseInstance(c1).toPrintable());

    }
}

class Customer{
    int id;
    boolean flag = false;
}

压缩指针:

  • java -XX: +PrintCommandLineFlags-version 展示默认适用情况
  • 默认开启压缩指针(-XX:+UseCompressedClassPointers), 12 + 4(对齐填充) 一个对象 16个字节
  • 关闭压缩指针(-XX:-UseCompressedClassPointers),Mark Word 占8个字节, 类型占 8个字节,一共 16个字节

底层代码

oop.hpp:

// oop.hpp
class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark;
  union _metadata {
    Klass*      _klass;
    narrowKlass _compressed_klass;
  } _metadata;

  // Fast access to barrier set.  Must be initialized.
  static BarrierSet* _bs;

 public:
  markOop  mark() const         { return _mark; }
  markOop* mark_addr() const    { return (markOop*) &_mark; }

  void set_mark(volatile markOop m)      { _mark = m;   }

  void    release_set_mark(markOop m);
  markOop cas_set_mark(markOop new_mark, markOop old_mark);

  // Used only to re-initialize the mark word (e.g., of promoted
  // objects during a GC) -- requires a valid klass pointer
  void init_mark();

  Klass* klass() const;
  Klass* klass_or_null() const volatile;
  Klass** klass_addr();
  narrowKlass* compressed_klass_addr();

  void set_klass(Klass* k);

  // For klass field compression
  int klass_gap() const;
  void set_klass_gap(int z);
  // For when the klass pointer is being used as a linked list "next" field.
  void set_klass_to_list_ptr(oop k);
  oop list_ptr_from_klass();

  // size of object header, aligned to platform wordSize
  static int header_size()          { return sizeof(oopDesc)/HeapWordSize; }

  // Returns whether this is an instance of k or an instance of a subclass of k
  bool is_a(Klass* k)  const;
  ......
}

markOop.hpp:

#ifndef SHARE_VM_OOPS_MARKOOP_HPP
#define SHARE_VM_OOPS_MARKOOP_HPP

#include "oops/oop.hpp"

// The markOop describes the header of an object.
//
// Note that the mark is not a real oop but just a word.
// It is placed in the oop hierarchy for historical reasons.
//
// Bit-format of an object header (most significant first, big endian layout below):
//
//  32 bits:
//  --------
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
//             size:32 ------------------------------------------>| (CMS free block)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)
//
//  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
//  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
//  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
//  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值