在做JVM内存调优的时候,咱们免不了须要去计算对象的大小。计算对象大小又要思考是一般对象还是数组对象,因为一般对象与数组对象的对象头存在些许差别。而且自JDK6当前,为了节俭内存、进步运行效率,又引入了新的技术:指针压缩。更加剧了计算对象大小的难度。
这篇文章就来深入分析如何计算对象大小,确保计算的后果与理论状况不差一个字节。
对象构造
对象构造想必深刻学习过JVM的人都晓得,它分为三大部分:对象头、实例数据、对齐填充。其中对象头又分为三个局部:Mark Word、类型指针、数组长度。其实对象头还有第四局部,这是目前你看到的书、视频都没有提到的,对象头也有对齐填充局部,这个局部也不是肯定会有,只有数组对象在未开启指针压缩的状况下才会呈现。是不是一脸懵,那就持续往后看。
指针压缩
看到这四个字是不是一堆的问号:这是什么?这怎么实现的?为什么说它节俭了内存?……咱们就来搞清楚这些个问题。
咱们先达成观点上的统一:所有的对象都是以8字节对齐的。当初我有3个对象:test1(16字节)、test2(32字节)、test3(24字节),为了便于阐明,如果这三个对象两头没有其余对象,那他们的内存地址是:
test1 = 0x0000 0 000(0字节 ~ 16字节) test2 = 0x0001 0 000(16字节 ~ 48字节) test3 = 0x0011 0 000(48字节 ~ 72字节)
大家发现法则了吗,所有对象指针的后三位都是0,这就是指针压缩的实现原理。开启指针压缩后,JVM会将对象指针的后三位给截去,如果test2 = 0x10000,在开启指针后就变成了test2=0x10,在应用的时候将后三位的0补回去,即test2=0x10 000。
因为开启了指针压缩后,对象指针就变成了4字节(32位),加上补3位,共35位。即开启指针压缩后一个对象指针能示意的最大堆空间是2的35次方,即32G。
那读者能够想一想?如果我想裁减一个oop能示意的堆空间大小该怎么做呢?
上面咱们来看四种状况(一般对象-敞开指针压缩、一般对象-开启指针压缩、数组对象-敞开指针压缩、数组对象-开启指针压缩)下的对象大小是如何计算出来的。倡议读者写类似代码测试一下,这样能力了解得更粗浅。
测试代码:
package com.qimingnan.adjust;import org.openjdk.jol.info.ClassLayout;public class Test1 { int a = 10; int b = 20; static int[] arr = {0, 1, 2}; public static void main(String[] args) { Test1 test1 = new Test1(); System.out.printf(ClassLayout.parseInstance(test1).toPrintable()); System.out.printf(ClassLayout.parseInstance(arr).toPrintable()); }}
一般对象
一、未开启指针压缩
24B = 8B(Mark Word)+ 8B(KClass Pointer)+ 4B + 4B
二、开启指针压缩
24B = 8B(Mark Word)+ 4B(KClass Pointer)+ 4B(int a)+ 4B(int b)+ 4B(Padding)
数组对象
一、未开启指针压缩
40B = 8B(Mark Word)+ 8B(KClass Pointer)+ 4B(数组长度)+ 4B(头部Padding)+ 12B(3个int)+ 4B(对象Padding)
二、开启指针压缩
32B = 8B(Mark Word)+ 4B(KClass Pointer)+ 4B(数组长度)+ 12B(3个int)+ 4B(对象Padding)
如何计算对象大小大家学会了吗?
介绍一款工具,jol,大家在做测试的时候能够借助这个工具
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.10</version></dependency>
这篇文章有些中央可能不太好了解,读者有问题的话,留言发问吧。我都会一一回复。
后续我将为大家新开《手写JVM》的系列专栏,如果你对此感兴趣的话,那么就关注我吧。