【JUC】Java对象内存布局和对象头

news/2024/11/28 6:35:31/

【JUC】Java对象内存布局和对象头

文章目录

  • 【JUC】Java对象内存布局和对象头
    • 1. 对象的内存布局
      • 1.1 对象头
        • 1.1.1 对象标记
        • 1.1.2 类元信息/类型指针
      • 1.2 实例数据
      • 1.3 对齐填充
    • 2. 测试

1. 对象的内存布局

在 HotSpot 虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:

  1. 对象头(Header)
  2. 实例数据(Instance Data)
  3. 对齐填充(Padding)

image-20230521224650686

1.1 对象头

对象头包含两个部分:

  • 对象标记(Mark Word)
  • 类元信息(又称类型指针)

1.1.1 对象标记

对象标记中包括以下信息:

  1. 对象的哈希码:在Java中,每个对象都有一个唯一的哈希码,用于识别该对象。
  2. 对象的锁状态:Java中的对象可以被加锁以进行线程同步。对象标记中的锁状态信息记录了该对象是否被加锁。
  3. GC相关信息:Java的垃圾回收器需要知道每个对象是否可达,以便决定是否回收该对象。对象标记中存储了与垃圾回收相关的信息,例如对象是否被标记为可达或已经被回收。

image-20230521230227552

image-20230521234157902

在64位系统中,Mark Word 占了8个字节,类型指针占了8个字节,一共是16个字节。

问:创建一个Object对象占用了多少字节?

答:一个普通的object对象没有实例数据,所以只有对象标记和类型指针加起来的的大小,也就是16字节。

Mark Word 被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word 里存储的数据会随着锁标志位的变化而变化。


1.1.2 类元信息/类型指针

**类型指针(Class Pointer)**就是用来指向该对象的类元数据信息的指针。它指向的是该对象所属的类的Class对象。这个指针是一个内存地址,可以用来确定该对象所属的类以及该类的方法和属性等信息。

在32位的JVM中,Class Pointer占用4个字节;在64位的JVM中,Class Pointer占用8个字节。它是对象头中比较重要的一个字段,因为它可以帮助虚拟机确定这个对象是哪个类的示例,从而进行方法调用、对象实例化等操作。

image-20230521231458776


对象头有多大?

答:在64位系统中,mark wordclass pointer 都占8个字节,所以对象头的大小为16个字节。


1.2 实例数据

存放类的属性(Field)数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。

示例:

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

创建一个 Customer 对象,实例数据(int占4个字节,boolean占1个字节)共5个字节


1.3 对齐填充

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

如 1.2 示例所示:创建一个 Customer 对象,对象头16个字节,实例数据5个字节,所以对象头+实例数据=21个字节。对齐填充之后,对象的大小为24个字节。


2. 测试

使用 JOL(Java Object LayOut)分析对象在JVM的大小和分布

image-20230522000335455

为什么这里类型指针大小是4字节而不是8字节呢?之后会说明。


为什么GC年龄最大为15?

因为GC年龄采用4位bit存储,所以最大为15,例如 MaxTenuringThreshold 参数默认值就是15。如果我们将其改成16会怎样?

设置jvm参数 -XX:MaxTenuringThreshold=16 ,运行任意一个程序,结果如下:

image-20230522001012200


为什么运行结果中类型指针的大小是4字节而不是8字节呢?

image-20230522002426979

在控制台中输入 java -XX:+PrintCommandLineFlags -version 指令,回车,控制台显示如下内容:

image-20230522001415163

-XX:+UseCompressedClassPointers 表示开启了类型指针压缩,以节约空间。

默认配置:启动了压缩指针,-XX:+UseCompressedClassPointers,12 + 4(对齐填充) = 一个对象16字节。

手动配置:关闭了压缩指针,-XX:-UseCompressedClassPointers, 8 + 8 = 一个对象16字节。

乍看之下好像压了等于没压,实际上不是:

一个Person类中,有两个boolean类型的成员变量。

默认配置:启动了压缩指针,对象头是12个字节,实例数据2个字节,那么这个对象大小就是 12 + 2 + 2(对齐填充) = 16字节。

手动配置:关闭了压缩指针,对象头是16个字节,实例数据2个字节,那么这个对象大小就是 16 + 2 + 6(对齐填充) = 24字节。


http://www.ppmy.cn/news/81572.html

相关文章

Windows远程访问本地 jupyter notebook服务

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 前言 Jupyter Notebook,它是一个交互式的数据科学和计算环境,支持多种编程语言&#…

【Linux】线程详解之线程控制

文章目录 POSIX线程库创建线程线程ID及进程地址空间布局线程等待pthread_join 线程终止pthread_exit函数pthread_cancel函数 线程分离理解pthread库 POSIX线程库 POSIX线程(英语:POSIX Threads,常被缩写为Pthreads)是POSIX的线程标…

一星期学MySQL day3

文章目录 约束常用约束外键约束删除/更新行为 多表查询多表关系一对多多对多一对一 查询内连接查询外连接查询自连接查询联合查询 union, union all注意事项 子查询标量子查询列子查询行子查询表子查询 事务四大特性ACID并发事务 约束 分类: 约束描述关键字非空约…

第十四章 Productions最佳实践 - 创建或编辑路由进程

文章目录 第十四章 Productions最佳实践 - 创建或编辑路由进程创建或编辑路由进程添加业务服务测试界面部署界面 第十四章 Productions最佳实践 - 创建或编辑路由进程 创建或编辑路由进程 要使新接口能够与路由引擎一起使用,必须向生产添加路由进程: …

Fourier分析入门——第8章——Fourier系数的统计描述

目录 第 8章 Fourier系数的统计描述 8.1 引言 8.2 统计假设 8.3 Fourier系数对噪声的均值和方差 8.4 Fourier系数对噪声信号的概率分布 8.5 随机信号的Fourier系数分布 8.6 信号平均 第 8章 Fourier系数的统计描述 8.1 引言 上一章通过假设离散函数是通过对连续函数定期…

设计模式之里氏替换原则

里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计的一个重要原则,由 Barbara Liskov 在 1987 年提出。它是针对继承的设计原则,它的核心思想是:继承应该确保父类对象能够在不知道子类对象的…

K8s Pod 无法启动!5个常见问题总结

为了避免业务停机,你需要不断完善你的排障技能, 定期对整个 Kubernetes 集群进行调试和故障排除对运维服务稳定至关重要。故障排除包括识别、诊断和解决 Kubernetes 集群、节点、Pod、容器和其他资源中的各类问题。 由于 Kubernetes 是一个复杂的系统&a…

程序员写代码时,有哪些编程技巧呢?

编程技巧确实可以帮助提高代码的可读性和效率。以下是一些可能有用的编程技巧: 1. 函数式编程:利用函数式编程思想,可以简化代码,减少副作用,并使代码更易于测试和维护。 2. 面向对象编程:面向对象编程可…