脚本宝典收集整理的这篇文章主要介绍了Java对象内存模型,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、 实例数据(Instance Data)和对齐填充(Padding)。
在 JVM 中,Java对象保存在堆中时,由以下三部分组成:
对象头包括两部分信息,第一部分用于存储对象自身的运行时数据(MarkWord), 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时 间戳等。对象头的另外一部分是类型指针(Klass Pointer),即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
// oopDesc hotspot对象头
volatile markOop _mark;
union _metadata {
wideKlassOop _klass;
narrowOop _comPressed_klass;
} _metadata;
Mark Word在32位JVM中的长度是32bIT,在64位JVM中长度是64bit。
markword组成内容基本一致。
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.8</version>
</dependency>
// 打印无属性对象
System.out.PRintln(ClassLayout.parseInstance(new Object()).toPrintable());
// 对象信息
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance Size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
对于普通无属性对象,一共占用16字节,其中对象头markword占用8个字节,类型指针占用4个字节,剩余4个字节用于对齐填充,没有实例数据。
JDK8版本默认开启指针压缩(-XX:+UseCompressedOops
),如果关闭指针压缩,类型指针占用8个字节,不再需要对齐填充。
// 打印数组
System.out.println(ClassLayout.parseInstance(new int[]{}).toPrintable());
// 对象信息
[I object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363)
12 4 (object header) // 数组长度 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
16 0 int [I.<elements> N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
对于数组对象,类型指针后有4个字节记录数组长度。因为虚拟机可以通过普通Java对象的元数据信息确定Java对象大小,如果数组长度不确定,则无法推断出数组对象大小。
// 打印有属性对象
System.out.println(ClassLayout.parseInstance(new User()).toPrintable());
class User {
int id; // 4B
String name; // 4B 未经类型压缩为8B
byte b; // 1B
Object o; // 4B 未经类型压缩为8B
}
// 对象信息
com.lzp.java.jvm.memory.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
// 对象头 12字节
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 65 cc 00 f8 (01100101 11001100 00000000 11111000) (-134165403)
// 实例数据
12 4 int User.id 0
16 1 byte User.b 0
17 3 (alignment/padding gap) // 对齐
20 4 java.lang.String User.name null
24 4 java.lang.Object User.o null
// 对齐填充
28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
通过这一节的分析,我们可以比较轻松地估计出一个对象的大小,即对象头+实例数据+对象填充,得到一个8字节倍数的值。
在Mark Word中可以发现标记对象分代年龄的分配的空间是4bit,而4bit能表示的最大数就是2^4-1 = 15。
通过指针压缩,类型指针、对象引用等由8字节转为4个字节。降低对象占用的内存大小,顺便减轻GC压力;当指针移动时,减少带宽损耗。
以上是脚本宝典为你收集整理的Java对象内存模型全部内容,希望文章能够帮你解决Java对象内存模型所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。