龙空技术网

Java面试题-对象的内存布局

贵哥说Java创业 53

前言:

而今兄弟们对“java 对象内存”可能比较关心,兄弟们都需要学习一些“java 对象内存”的相关文章。那么小编也在网络上网罗了一些关于“java 对象内存””的相关资讯,希望小伙伴们能喜欢,朋友们一起来了解一下吧!

HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

对象头:比如 hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间,数组长度(数组对象)等。Java对象头一般占有2个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit,在64位虚拟机中,1个机器码是8个字节,也就是64bit),但是 如果对象是数组类型,则需要3个机器码,因为JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。实例数据:存放类的属性数据信息,包括父类的属性信息;对齐填充:由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐;对象头

HotSpot虚拟机的对象头包括两部分信息。

第一部分是“Mark Word”,用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。它是实现轻量级锁和偏向锁的关键这部分数据的长度在32位和64位的虚拟机(暂不考虑开启压缩指针的场景)中分别为32个Bits和64个Bits,官方称它为“Mark Word”。

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

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

32位虚拟机

锁状态

25bit

4bit

1bit

2bit

23bit

2bit

是否偏向锁(是否禁用偏向)

锁标志位

无锁态

对象的hashCode

分代年龄

0

01

轻量级锁

指向栈中锁记录的指针

00

重量级锁

指向Monitor的指针

10

GC标记

11

偏向锁

线程ID

Epoch

分代年龄

1

01

现在我们虚拟机基本是64位的,而64位的对象头有点浪费空间,JVM默认会开启指针压缩,所以基本上也是按32位的形式记录对象头的。

手动设置-XX:+UseCompressedOops

哪些信息会被压缩?

1.对象的全局静态变量(即类属性)

2.对象头信息:64位平台下,原生对象头大小为16字节,压缩后为12字节

3.对象的引用类型:64位平台下,引用类型本身大小为8字节,压缩后为4字节

4.对象数组类型:64位平台下,数组类型本身大小为24字节,压缩后16字节

当heap size堆内存大于32GB是用不了压缩指针的,对象引用会额外占用20%左右的堆空间,也就意味着要38GB的内存才相当于开启了指针压缩的32GB堆空间。

这是为什么呢?因为不管是32bit的机器还是64bit的机器,java对象都是8byte对齐的,而类是java中的基本单位,对应的堆内存中都是一个一个的对象。

标签: #java 对象内存