返回顶部
首页 > 资讯 > 后端开发 > Python >详解Java对象的内存布局
  • 727
分享到

详解Java对象的内存布局

2024-04-02 19:04:59 727人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录前言对象内存构成对象头实例数据对齐数据结尾前言 今天来讲些抽象的东西 -- 对象头,因为我在学习的过程中发现很多地方都关联到了对象头的知识点,例如jdk中的 synchroniz

前言

今天来讲些抽象的东西 -- 对象头,因为我在学习的过程中发现很多地方都关联到了对象头的知识点,例如jdk中的 synchronized优化JVM 中对象年龄升级等等。要深入理解这些知识的原理,了解对象头的概念很有必要,而且可以为后面分享 synchronized 原理和 JVM 知识的时候做准备。

对象内存构成

Java 中通过 new 关键字创建一个类的实例对象,对象存于内存的堆中并给其分配一个内存地址,那么是否想过如下这些问题:

  • 这个实例对象是以怎样的形态存在内存中的?
  • 一个Object对象在内存中占用多大?
  • 对象中的属性是如何在内存中分配的?

在 JVM 中,Java对象保存在堆中时,由以下三部分组成:

  • 对象头(object header):包括了关于堆对象的布局、类型、GC状态、同步状态和标识哈希码的基本信息。Java对象和vm内部对象都有一个共同的对象头格式。
  • 实例数据(Instance Data):主要是存放类的数据信息,父类的信息,对象字段属性信息。
  • 对齐填充(Padding):为了字节对齐,填充的数据,不是必须的。

对象头

我们可以在Hotspot官方文档中找到它的描述(下图)。从中可以发现,它是Java对象和虚拟机内部对象都有的共同格式,由两个字(计算机术语)组成。另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。

它里面提到了对象头由两个字组成,这两个字是什么呢?我们还是在上面的那个Hotspot官方文档中往上看,可以发现还有另外两个名词的定义解释,分别是 mark Word 和 klass pointer。

从中可以发现对象头中那两个字:第一个字就是 mark word,第二个就是 klass pointer。

Mark Word

用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。

Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit。我们打开openjdk的源码包,对应路径/openjdk/hotspot/src/share/vm/oops,Mark Word对应到c++的代码markOop.hpp,可以从注释中看到它们的组成,本文所有代码是基于Jdk1.8。

Mark Word在不同的锁状态下存储的内容不同,在32位JVM中是这么存的

在64位JVM中是这么存的

虽然它们在不同位数的JVM中长度不一样,但是基本组成内容是一致的。

  • 锁标志位(lock):区分锁状态,11时表示对象待GC回收状态, 只有最后2位锁标识(11)有效。
  • biased_lock:是否偏向锁,由于无锁和偏向锁的锁标识都是 01,没办法区分,这里引入一位的偏向锁标识位。
  • 分代年龄(age):表示对象被GC的次数,当该次数到达阈值的时候,对象就会转移到老年代。
  • 对象的hashcode(hash):运行期间调用System.identityHashCode()来计算,延迟计算,并把结果赋值到这里。当对象加锁后,计算的结果31位不够表示,在偏向锁,轻量锁,重量锁,hashcode会被转移到Monitor中。
  • 偏向锁的线程ID(JavaThread):偏向模式的时候,当某个线程持有对象的时候,对象这里就会被置为该线程的ID。 在后面的操作中,就无需再进行尝试获取锁的动作。
  • epoch:偏向锁在CAS锁操作过程中,偏向性标识,表示对象更偏向哪个锁。
  • ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针。当锁获取是无竞争的时,JVM使用原子操作而不是OS互斥。这种技术称为轻量级锁定。在轻量级锁定的情况下,JVM通过CAS操作在对象的标题字中设置指向锁记录的指针。
  • ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针。如果两个不同的线程同时在同一个对象上竞争,则必须将轻量级锁定升级到Monitor以管理等待的线程。在重量级锁定的情况下,JVM在对象的ptr_to_heavyweight_monitor设置指向Monitor的指针。

Klass Pointer

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

实例数据

如果对象有属性字段,则这里会有数据信息。如果对象无属性字段,则这里就不会有数据。根据字段类型的不同占不同的字节,例如boolean类型占1个字节,int类型占4个字节等等;

对齐数据

对象可以有对齐数据也可以没有。默认情况下,Java虚拟机堆中对象的起始地址需要对齐至8的倍数。如果一个对象用不到8N个字节则需要对其填充,以此来补齐对象头和实例数据占用内存之后剩余的空间大小。如果对象头和实例数据已经占满了JVM所分配的内存空间,那么就不用再进行对齐填充了。

所有的对象分配的字节总SIZE需要是8的倍数,如果前面的对象头和实例数据占用的总SIZE不满足要求,则通过对齐数据来填满。

为什么要对齐数据?字段内存对齐的其中一个原因,是让字段只出现在同一CPU的缓存行中。如果字段不是对齐的,那么就有可能出现跨缓存行的字段。也就是说,该字段的读取可能需要替换两个缓存行,而该字段的存储也会同时污染两个缓存行。这两种情况对程序的执行效率而言都是不利的。其实对其填充的最终目的是为了计算机高效寻址。

至此,我们已经了解了对象在堆内存中的整体结构布局,如下图所示

Talk is cheap, show me code

概念的东西是抽象的,你说它是这样组成的,就真的是吗?学习是需要持怀疑的态度的,任何理论和概念只有自己证实和实践之后才能接受它。还好 openjdk 给我们提供了一个工具包,可以用来获取对象的信息和虚拟机的信息,我们只需引入 jol-core 依赖,如下


<dependency>
  <groupId>org.openjdk.jol</groupId>
  <artifactId>jol-core</artifactId>
  <version>0.8</version>
</dependency>

jol-core 常用的三个方法

  • ClassLayout.parseInstance(object).toPrintable():查看对象内部信息.
  • GraphLayout.parseInstance(object).toPrintable():查看对象外部信息,包括引用的对象.
  • GraphLayout.parseInstance(object).totalSize():查看对象总大小.

普通对象

为了简单化,我们不用复杂的对象,自己创建一个类 D,先看无属性字段的时候


public class D {
}

通过 jol-core 的 api,我们将对象的内部信息打印出来


public static void main(String[] args) {
    D d = new D();
    System.out.println(ClassLayout.parseInstance(d).toPrintable());
}

最后的打印结果为

可以看到有 OFFSET、SIZE、TYPE DESCRIPTION、VALUE 这几个名词头,它们的含义分别是

  • OFFSET:偏移地址,单位字节;
  • SIZE:占用的内存大小,单位为字节;
  • TYPE DESCRIPTION:类型描述,其中object header为对象头;
  • VALUE:对应内存中当前存储的值,二进制32位;

可以看到,d对象实例共占据16byte,对象头(object header)占据12byte(96bit),其中 mark word占8byte(64bit),klass pointe 占4byte,另外剩余4byte是填充对齐的。

这里由于默认开启了指针压缩,所以对象头占了12byte,具体的指针压缩的概念这里就不再阐述了,感兴趣的读者可以自己查阅下官方文档。jdk8版本是默认开启指针压缩的,可以通过配置vm参数开启关闭指针压缩,-XX:-UseCompressedOops

如果关闭指针压缩重新打印对象的内存布局,可以发现总SIZE变大了,从下图中可以看到,对象头所占用的内存大小变为16byte(128bit),其中 mark word占8byte,klass pointe 占8byte,无对齐填充。

开启指针压缩可以减少对象的内存使用。从两次打印的D对象布局信息来看,关闭指针压缩时,对象头的SIZE增加了4byte,这里由于D对象是无属性的,读者可以试试增加几个属性字段来看下,这样会明显的发现SIZE增长。因此开启指针压缩,理论上来讲,大约能节省百分之五十的内存。jdk8及以后版本已经默认开启指针压缩,无需配置。

数组对象

上面使用的是普通对象,我们来看下数组对象的内存布局,比较下有什么异同


public static void main(String[] args) {
    int[] a = {1};
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
}

打印的内存布局信息,如下

可以看到这时总SIZE为共24byte,对象头占16byte,其中Mark Work占8byte,Klass Point 占4byte,array length 占4byte,因为里面只有一个int 类型的1,所以数组对象的实例数据占据4byte,剩余对齐填充占据4byte。

结尾

经过以上的内容我们了解了对象在内存中的布局,了解对象的内存布局和对象头的概念,特别是对象头的Mark Word的内容,在我们后续分析 synchronize 锁优化 和 JVM 垃圾回收年龄代的时候会有很大作用。

JVM中大家是否还记得对象在Suvivor中每熬过一次MinorGC,年龄就增加1,当它的年龄增加到一定程度后就会被晋升到老年代中,这个次数默认是15岁,有想过为什么是15吗?在Mark Word中可以发现标记对象分代年龄的分配的空间是4bit,而4bit能表示的最大数就是2^4-1 = 15。

以上就是详解Java对象的内存布局的详细内容,更多关于Java对象内存布局的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解Java对象的内存布局

本文链接: https://lsjlt.com/news/124308.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 详解Java对象的内存布局
    目录前言对象内存构成对象头实例数据对齐数据结尾前言 今天来讲些抽象的东西 -- 对象头,因为我在学习的过程中发现很多地方都关联到了对象头的知识点,例如JDK中的 synchroniz...
    99+
    2024-04-02
  • Java对象存储内存布局详解
    目录Java对象存储内存布局Java对象的访问定位Java对象的创建过程Java对象分配内存是否线程安全Java类实例化顺序Java对象存储内存布局 众所周知,Java是一门面向对象...
    99+
    2023-05-18
    Java对象存储内存布局 Java内存布局 Java对象内存
  • Java中对象的内存布局
    这篇文章主要讲解了“Java中对象的内存布局”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中对象的内存布局”吧!作为一名Java程序员,我们在日常工作中使用这款面向对象的编程语言时,...
    99+
    2023-06-15
  • Java对象的内存布局详细介绍
    目录一、对象头1)、Mark Word2)、类型指针3)、数组长度(只有数组对象才有)二、实例数据三、对齐填充四、使用JOL工具分析对象内存布局在HotSpot虚拟机中,对象在内存中...
    99+
    2023-02-13
    Java对象内存布局 Java内存布局 Java对象内存
  • c++对象内存布局示例详解
    目录前言继承对象的内存布局具有多重继承和虚拟功能的对象的内存布局总结前言 了解你所使用的编程语言究竟是如何实现的,对于C++程序员可能特别有意义。首先,它可以去除我们对于所使用语言的...
    99+
    2024-04-02
  • 详解Java对象创建的过程及内存布局
    目录一、对象的内存布局二、对象的创建过程三、对象访问定位四、参考一、对象的内存布局 对象头 对象头主要保存对象自身的运行时数据和用于指定该对象属于哪个类的类型指针。 实例数据 保存对...
    99+
    2024-04-02
  • Java 对象在 JVM 中的内存布局超详细解说
    目录一、new 对象的几种说法二、Java 对象在内存中的存在形式1. 栈帧(Frame)2. 对象在内存中的存在形式 ①3. 对象中的方法存储在那儿?4. Java 对象在内存中的...
    99+
    2024-04-02
  • Java对象的内存布局全流程
    目录对象内存布局对象占用内存空间证明对象内存布局开始先抛出一个问题:一个对象o,Object o = new Object();创建完成后会占用多少字节的内存? 要能回答这个问题,就...
    99+
    2024-04-02
  • 怎么在Java中布局对象内存
    怎么在Java中布局对象内存?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。常用的java框架有哪些1.SpringMVC,Spring Web MVC是一种基于Java的实...
    99+
    2023-06-14
  • Java对象在内存中的布局是如何实现的?
    1、-XX:FieldsAllocationStyle 对象在内存中的布局首要相关配置就是FieldsAllocationStyle,这个配置有3个可选值,即0、1、2。当值为2的时...
    99+
    2024-04-02
  • 浅析Python的对象拷贝和内存布局
    目录前言Python 对象的内存布局牛刀小试查看对象的内存地址copy模块可变和不可变对象与对象拷贝代码片段分析撕开 Python 对象的神秘面纱总结前言 在本篇文章当中主要给大家介...
    99+
    2022-12-19
    Python对象拷贝 内存布局 Python对象拷贝 Python 内存布局
  • Python的对象拷贝和内存布局怎么实现
    前言你知道下面一些程序片段的输出结果吗?a = [1, 2, 3, 4] b = a print(f"{a = } \t|\t {b = }") a[0] = 100 print(f"{a = } \t|\t ...
    99+
    2023-05-15
    Python
  • Python的对象拷贝和内存布局如何实现
    今天小编给大家分享一下Python的对象拷贝和内存布局如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。前言你知道下面一...
    99+
    2023-07-06
  • Java面向对象和内存分析图文详解
    一、Java类 类是面向对象编程中最基本的单位。 Java中的类包含三个内容,分别是: 属性 属性又叫成员变量。 属性用于定义类或类对象的数据(静态特征)。 ...
    99+
    2024-04-02
  • Java对象创建内存案例解析
    Java对象创建内存图解析 1. 栈 Java栈的区域很小 , 特点是存取的速度特别快,栈存储的特点是, 先进后出,存储速度快的原因: 栈内存, 通过 栈指针'来创...
    99+
    2024-04-02
  • Java面向对象之内部类详解
    目录前言内部类概述内部类的分类成员内部类局部内部类前言 在 Java 中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。本篇博客将总结内部类的使用。 内部类概述...
    99+
    2024-04-02
  • Android抽象布局——include、merge 、ViewStub详解
    在布局优化中,Androi的官方提到了这三种布局<include />、<merge />、<ViewStub />,并介绍了这三种布局各有...
    99+
    2022-06-06
    布局 viewstub merge include Android
  • java存储以及java对象创建的流程(详解)
    java存储:1)寄存器:这是最快的存储区,位于处理器的内部。但是寄存器的数量有限,所以寄存器根据需求进行分配。我们不能直接进行操作。2)堆栈:位于通用RAM中,可以通过堆栈指针从处理器那里获取直接支持。堆栈指针往下移动,则分配新的内存。网...
    99+
    2023-05-31
    java 存储 对象创建
  • Linux内存布局是怎样的
    这篇“Linux内存布局是怎样的”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Linux内存布局是怎样的”文章吧。我们先来看...
    99+
    2023-06-28
  • Java Date 对象详解
    一、 Java Date 对象 Java Date 类表示日期和时间,可以存储从1970年1月1日零时(格林威治标准时间)起累计的毫秒数。Date 对象支持日期的计算和显示,并且可以与其他系统进行时间...
    99+
    2023-10-01
    java 开发语言 jvm
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作