JVM의 객체 메모리 레이아웃

이 기사에서는 객체의 메모리 레이아웃과 힙의 객체에 액세스하는 방법을 소개합니다.

머리말

Object object = new Object() 이 문장에 대한 이해도를 알려주시겠습니까? 일반적으로 JDK8의 기본 상황에 따라 새 개체가 차지하는 메모리 공간의 양입니다.

  • 객체의 위치를 ​​힙에 저장하는 것은 이전에 논의된 바 있다(기존 상황: 에덴동산 → S0/S1 → 구세대)
  • 이제 레이아웃에 대해 이야기합시다. 즉, 개체의 구성은 무엇입니까? 머리와 몸?

이미지-20230128003114228.png

개체 메모리 레이아웃

HotSpot 가상 머신에서 힙 메모리에 있는 객체의 스토리지 레이아웃은 객체 헤더(Header), 인스턴스 데이터(Instance Data) 및 정렬 패딩(Padding)의 세 부분으로 나눌 수 있습니다.

1) 객체 헤더

객체 헤더에는 데이터의 두 부분이 포함됩니다.

  • 런타임 속성 Mark Word
  • 유형 포인터 클래스 포인터

개체가 배열인 경우 배열의 길이도 기록해야 합니다. 아래 그림과 같이:

이미지-20230128003700443.png

런타임 메타데이터 Mark World

  • HashCode, 객체 나이, 잠금 상태 플래그, 스레드가 보유한 잠금 등과 같은 정보를 저장합니다.

  • 64비트 시스템에서 Mark Word는 81바이트를 차지하고 타입 포인터는 81바이트[ 포인터 압축 이 활성화된 경우 4바이트], 총 16(12)바이트를 차지합니다.

  • 새 개체에 인스턴스 데이터가 없으면 16바이트입니다. [기본값은 포인터 압축 시작 -XX:+UseCompressedClassPointers, 정렬 패딩 4바이트]

  • Mark Word의 저장 구조는 아래 그림과 같습니다.

    이미지-20230128004733823.png

유형 포인터 클래스 포인터

  • 메서드 영역의 클래스 메타 정보(개체 템플릿)를 가리키면 가상 머신은 이 포인터를 사용하여 개체가 인스턴스인 클래스를 결정합니다.

2) 인스턴스 데이터

  • 객체에 의해 실제로 저장되는 유효한 정보, 즉 클래스에 정의된 다양한 유형의 속성(부모 클래스에서 상속되고 자체적으로 정의된 것 포함)입니다.
  • 인스턴스 데이터 스토리지에는 특정 규칙이 있습니다.
    1. 너비가 같은 필드는 항상 함께 할당됩니다.
    2. 상위 클래스에 정의된 변수는 하위 클래스 앞에 나타납니다.
    3. CompactFields 매개 변수가 true인 경우(기본값은 true): 하위 클래스의 좁은 변수가 상위 클래스 변수의 간격에 삽입될 수 있습니다.

3) 패딩 정렬

  • 虚拟机要求对象起始地址必须是8字节的整数倍【具体原因这里就不赘述了,读者可自行查阅相关资料】。
  • 填充数据不是必须存在的,仅仅是为了字节对齐这部分内存按8字节补充对齐

4)JOL验证

引入JOL依赖

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
复制代码

使用JOL查看Object对象内部细节

이미지-20230128011130683.png

使用JOL查看Customer对象内部细节

public class Customer {
    int id;
    boolean flag = false;
    public static void main(String[] args) {
        System.out.println(ClassLayout.parseInstance(new Customer()).toPrintable());
    }
}
复制代码

이미지-20230128011916396.png

  • 7→填充的字节数
  • 声明一个Customer的实例,只有一个对象头的实例对象,12字节(开启压缩指针)+ 4字节[int] + 1字节[boolean]=17字节,此时需要对齐填充到24字节

默认开启指针压缩的 -XX:+UseCompressedClassPointers

  • 12 + 4(对齐填充) == 一个对象16字节(不算实例数据)

手动关闭指针压缩 -XX:-UseCompressedClassPointers

  • 8 + 8 == 16字节(不算实例数据)

图示对象的内存布局

이미지-20230128012158496.png

对象的访问定位

创建对象是为了后续使用该对象,那么JVM是如何通过栈帧中的对象引用访问到其内部的对象实例的呢?

  • 通过栈上的reference访问

而reference类型只是一个指向对象的引用,并没有定义这个引用应该通过什么方式去定位、访问到堆中对象的具体位置,而JVM中主流的对象访问方式主要有使用句柄直接指针两种方式。

1)句柄访问

Java堆中可能会划分出一块内存来作为句柄池,而reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息。

  • 优点:reference中存储稳定句柄地址,对象被移动(垃圾收集时候移动对象很普遍)时会改变句柄中的实例数据指针即可,reference本身不需要被修改
  • 缺点:需要多占用一些空间

이미지-20230128113610266.png

2)直接指针

  • HotSpot使用该方式。

직접 포인터 액세스를 사용하는 경우 Java 힙에서 객체의 메모리 레이아웃은 액세스 유형 데이터의 관련 정보를 배치하는 방법을 고려해야 합니다. 객체 주소는 참조에 직접 저장됩니다. 객체 자체에만 액세스하는 경우에는 추가 간접 액세스 오버헤드가 필요하지 않습니다. .

이미지-20230128114455925.png

Supongo que te gusta

Origin juejin.im/post/7193547233650802746
Recomendado
Clasificación