「对象头java」对象头大生孩子会不会也头大

博主:adminadmin 2022-12-02 19:39:07 68

本篇文章给大家谈谈对象头java,以及对象头大生孩子会不会也头大对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

JVM对象头(header)

HotSpot 虚拟机的对象头包括两部分信息:Mark Word(标记字段)和 Klass Pointer(类型指针)

      1. Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等。JVM 对象头一般占用两个机器码,在 32-bit JVM 上占用 64bit, 在 64-bit JVM 上占用 128bit 即 16 bytes(暂不考虑开启压缩指针的场景)。

另外,如果对象是一个 Java 数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通 Java 对象的元数据信息确定 Java 对象的大小,但是从数组的元数据中无法确定数组的大小。

对象需要存储的运行时数据很多,其实已经超出了32、64位 Bitmap 结构所能记录的限度,但是对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word 被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。

例如在 32 位的HotSpot 虚拟机中对象未被锁定的状态下,Mark Word 的 32个Bits 空间中的 25Bits 用于存储对象哈希码(HashCode),4Bits 用于存储对象分代年龄,2Bits 用于存储锁标志位,1Bit固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如下表所示。

怎么确定Java对象的大小

普通对象的结构如下,按64位机器的长度计算

1. 对象头(_mark), 8个字节

2. Oop指针,如果是32G内存以下的,默认开启对象指针压缩,4个字节

3. 数据区

4.Padding(内存对齐),按照8的倍数对齐

数组对象结构是

1. 对象头(_mark), 8个字节

2. Oop指针,如果是32G内存以下的,默认开启对象指针压缩,4个字节

3. 数组长度,4个字节

4. 数据区

5. Padding(内存对齐),按照8的倍数对齐

清楚了对象在内存的基本布局后,咱们说两种计算Java对象大小的方法

1. 通过java.lang.instrument.Instrumentation的getObjectSize(obj)直接获取对象的大小

2. 通过sun.misc.Unsafe对象的objectFieldOffset(field)等方法结合反射来计算对象的大小

java.lang.instrument.Instrumentation.getObjectSize()的方式

先讲讲java.lang.instrument.Instrumentation.getObjectSize()的方式,这种方法得到的是Shallow Size,即遇到引用时,只计算引用的长度,不计算所引用的对象的实际大小。如果要计算所引用对象的实际大小,可以通过递归的方式去计算。

java.lang.instrument.Instrumentation的实例必须通过指定javaagent的方式才能获得,具体的步骤如下:

1. 定义一个类,提供一个premain方法: public static void premain(String agentArgs, Instrumentation instP)

2. 创建META-INF/MANIFEST.MF文件,内容是指定PreMain的类是哪个: Premain-Class: sizeof.ObjectShallowSize

3. 把这个类打成jar,然后用java -javaagent XXXX.jar XXX.main的方式执行

有兴趣可以看下博主的:

一个Java对象到底占多大内存

第一问中,integer,Double是可以准确知道大小的 java中int double(包装类分别是Integer和Double,实际是一样的)分别占用的大小是4B 和8B 这里说的B,就是我们通常说的KB中的那个B,全称是Byte。 1B = 8bit。 所以我们通常会说int是32位,有时候又4字节 而String他是有char数组组成,一个长度为10的String,占用的char就是10个,一个char是2B。 所以看String占多少内存,需要知道String的长度 二问中:答案是可以的 摘自ObjectOutPutStream的API说明 FileOutputStream fos = new FileOutputStream("t.tmp"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeInt(12345); oos.writeObject("Today"); oos.writeObject(new Date()); oos.close();

Mark Word

先说一下synchronized的历史吧,在jdk1.6之前synchronized就是单纯一个重量级锁,在jdk1.6的时候,有一个叫Doug Lea的大哥看不过去了,明明很多情况都是不需要重量级锁的,只需要标记一下,所以这个大哥就开发一个ReentrantLock,无竞争的情况在java层面就完成了加锁解锁,这时候吧,jdk可能觉得面子上有点那啥,便开始优化synchronized,然后就产生了我们熟知的偏向锁,轻量级锁,重量级锁。

因为网上大部分的文章写mark word的解释基本都是基于32位jdk,这里我简单介绍一下64位jdk中,mark down的组成,和锁升级过程标志位的变化。

java的对象布局是由对象头、实例数据、数据对齐组成

但实际情况并不一定,后面我们引用下面的maven依赖来进行证明

发现没有数据填充的部分,这时候我们并不需要数据填充

(3)同理,我们将变量a删除后,没有了示例数据这一部分

对齐填充并不是必然存在的,也没有什么特别的意义,他仅仅起着占位符的作用,由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头正好是8字节的倍数,因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

对象头由两部分组成,一部分用于存储自身的运行时数据,称之为mark word,另外一部分是类型指针,及对象指向它的类元数据的指针。这里我们只研究一下mark word

OpenJdk官网对于mard word的定义

每个对象头的第一个字。通常是一组位字段,包括同步状态和标识哈希码。也可以是指向同步相关信息的指针(具有特征性的低位编码)。在GC期间,可以包含GC状态位。

我们再看hotspot的源码,里面有一段 注释

首先无锁、偏向锁、轻量锁大家应该都很清楚,但是图中的无锁可偏向和无锁不可偏向我在这里解释下,

我们知道偏向锁会在mark word中记录偏向线程的id,但是根据上图,我们可以发现,hashcode和偏向线程的id的位置是有冲突的,所以在不可偏向时就是我们计算了这个类的hashcode,这时候锁会直接升级成轻量级锁,后面我们会通过例子来证明。

这里我要先说明下,后面我们输出的mark word和我们期望的 在位置上是相反的,这是由于cpu的缘故,这里不做解释

jdk8中,有一个偏向锁的延迟开启,我们需要把延迟时间设置为0

-XX:BiasedLockingStartupDelay=0

首先我们可以看到 实例的hashcode和mark word里面标记的一样,并且现在是无锁不可偏向状态

下面不计算hashcode再看一下锁的标志位

关于对象头java和对象头大生孩子会不会也头大的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

The End

发布于:2022-12-02,除非注明,否则均为首码项目网原创文章,转载请注明出处。