Java - 对象的内存布局(64位HotSpot)

因为JVM虚拟机为HotSpot,以下谈的都是在64位HotSpot虚拟机中

对象内存布局

对象的内存布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)

注:数组对象中对象头部分多了数组长度Length,存储长度为4字节

在这里插入图片描述

其内存结构:

  • MarkWord:存储对象的hashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等
  • ClassPointer:指向对象对应的Class对象(类对象)的内存地址
  • InstanceData:具体的数据大小,如对象含有一个int成员变量,即为4字节

MarkWord

在openJDK源码中的 markOop.hpp 文件中看到可以看到下面,markOop.hpp文件一般都在目录 hotspot\src\share\vm\oops 下

在这里插入图片描述

32位与64位JVM有一些不同,现在都是使用64位虚拟机

在64位JVM中,MarkWord为8字节-64bit

不同的锁状态MarkWord会不同:

在这里插入图片描述

关于 锁的四种状态与锁升级过程 图文详解

当无锁态时,64bit分别为:

  • 25bit不使用
  • 31bit hashCode形式的对象的地址
  • 再后一位unused是cms_free:不是64位虚拟机就占用 0 bit,是64位就占用 1bit
  • Age为GC分代年龄,4bit,所以对象GC年龄最大为15
  • biased_lock偏向锁,1bit,0代表无锁,1代表偏向锁
  • lock锁信息,2bit,用于记录锁的状态

00—轻量锁
01—偏向锁/无锁,根据前面的biased_lock决定
10—重量锁
11—GC标记

注意:无锁态01是指没有加锁,而下面的无锁00是指CAS锁


ClassPointer

ClassPointer本来也是8字节,但64位JVM中默认压缩ClassPointer

通过cmd中java -XX:+PrintCommandLineFlags -version

在这里插入图片描述

其中:

  • UseCompressedClassPointers 类指针压缩:压缩class pointer,由8字节压缩至4字节,间接的提高内存的利用率
  • UseCompressedOops 普通对象指针压缩:通常64位JVM消耗的内存会比32位的大1.5倍,这是因为对象指针在64位架构下,长度会翻倍(更宽的寻址),通过UseCompressedOops 可以压缩指针

开启UseCompressedOops 也默认强制开启UseCompressedClassPointers,关闭UseCompressedOops 默认关闭UseCompressedClassPointers

JVM优化之压缩普通对象指针(CompressedOops)


其他

  • 实例数据(Instance Data)即对象中的包含的属性
Object o = new Object()

Object中Instance Data为0

MyObject o = new MyObject(3);
class MyObject{

    private int a;

    public MyObject(int a) {
        this.a = a;
    }
}

MyObject中InstanceData为4字节(int型数据为4字节)

  • Padding:将对象内存填充为8的倍数

为什么对象内存要为8的倍数?

目的是alignment,它允许以一些空间为代价加快内存访问。如果数据未对齐,则处理器需要在加载内存后进行一些转换才能访问它。
此外,垃圾回收简化(并加快)最小分配单元的大小

如果您有32位引用,则可以寻址最多2^32或4 GB的内存(实际上,尽管您获得的更少,但更像3.5 GB)。如果您有64位引用,则可以寻址2^64,即内存为terrabytes。但是,对于64位引用,一切都趋于放缓并占用更多空间。这是由于处理64位的32位处理器的开销,以及由于更少的空间和更多垃圾收集而导致所有处理器上的GC循环更多。
因此,创作者采取了一个中间立场,并决定使用35位参考,这样可以达到2^35或32 GB的内存,占用更少的空间,从而获得与32位参考相同的性能优势。这是通过读取一个32位参考并在读取时将其左移3位来完成的,并且在存储参考时将其右移3位。这意味着所有对象必须在2^3边界(8字节)上对齐。这些被称为压缩的普通对象指针或压缩的oops。

为什么不用36位引用来访问64 GB内存?那么,这是一个折衷。您需要大量浪费空间来处理16字节的对齐,并且据我所知,绝大多数处理器不会从16字节对齐中获得速度优势,而不是8字节对齐。
请注意,JVM不会打扰使用压缩的oops,除非最大内存设置为4 GB以上,而默认情况下它不是。实际上,您可以使用-XX:+UsedCompressedOops标志启用它们。

这是在32位虚拟机当天重新提供64位系统上的额外可用内存。据我所知,64位虚拟机没有限制。

来源:Java性能:权威指南,第8章

  • 数组对象还有一个Length段:4字节

通过JOL检验

Maven导入依赖

<!--JOL 内存布局工具-->
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.14</version>
    <scope>provided</scope>
</dependency>

测试类:

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

        //MyObject o = new MyObject(3);
        //int[] o = new int[4];
        Object o = new Object();

        System.out.println(ClassLayout.parseInstance(o).toPrintable());

        synchronized (o){
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }

    }
}

class MyObject{

    private int a;

    public MyObject(int a) {
        this.a = a;
    }
}

在这里插入图片描述

一共16字节,其中前8字节为MarkWord,8-12字节为ClassPointer,12-16为Padding填充为8的倍数

synchronized 锁信息就添加在MarkWord

MyObject o = new MyObject(3);

InstanceData为int型数据-4字节,即8+4+4=16,不需要填充
在这里插入图片描述

int[] o = new int[4];

数组对象Object Header多了一段Length=4,因为InstanceData为空,也是16字节
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值