Java虚拟机--你的对象有多大

如何计算对象大小

上文中,笔者提到了对象头,并且说到了对象头中的Mark Word在32位的机器中会占用4字节,在64位机器中占用8字节。那么,整个对象会占用多大内存呢?

带着这样的疑问,我们来实际的测量下,一个对象到底会占用多大内存?

在实际计算之前,我们先来普及下接口Instrumentation和其实现类InstrumentationImpl。

Instrumentation介绍:

java.lang.instrument.Instrumentation接口:它提供了丰富的对结构的等各方面的跟踪和对象大小的测量的API。

sun.instrument.IntrumentationImpl类:sun开头的,Instrumentation接口的实现类,构造方法为private,没有任何getInstance的方法。

使用Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在JVM上的程序。开发者就可以实现更为灵活的运行时虚拟机监控,这样的特性实际上提供了一种虚拟机级别支持的AOP实现方式,使得开发者无需对JDK做任何升级和改动,就可以实现某些AOP的功能了。

说的直白点,Instrumentation就是一个代理。在代码层面,java.lang.instrument.Instrumentation是接口,sun.instrument.InstrumentationImpl是其实现类。

说了这么多,那么Instrumentation应该怎么使用呢?

值得一体的是,Instrumentation不能像我们平常new对象的方式来实现使用,代码层面我们无法得到Instrumentation的实例对象。开发者需要提供premain函数,让虚拟机注入。此外,premain函数在 main函数运行前执行。简要说来就是如下几个步骤:

(1)编写premain函数

编写一个 Java 类,包含如下两个方法当中的任何一个
1. public static void premain(String agentArgs, Instrumentation inst); 
2. public static void premain(String agentArgs); 
其中,1 的优先级比 2 高,将会被优先执行(1 和 2 同时存在时,2 被忽略)。

在这个premain函数中,开发者可以进行对类的各种操作。inst是java.lang.instrument.Instrumentation 的实例,由JVM自动传入。

java.lang.instrument.Instrumentation是instrument包中定义的一个接口,也是这个包的核心部分,集中了其中几乎所有的功能方法,例如类定义的转换和类的操作等。

(2)class文件打成jar包

将这个Java类编译成class文件,再打成一个jar包,并在jar包中META-INF/MANIFEST.MF文件加入“ Premain-Class”来指定步骤1中编写的那个带有premain的Java类。

Premain-Class: java类的全限定类名

(3)运行

用命令行中输入如下命令:

java -javaagent:xxx.jar 被代理的类

说完了Instrumentation,接下来就用它来实际测量下对象的大小:

普通对象:

Instrumentation注入类:

public class ObjectSize {

    private static Instrumentation inst;

    public static void premain(String agentArgs, Instrumentation instP){
        inst = instP;
    }

    public static long sizeOf(Object obj){
        return inst.getObjectSize(obj);
    }
}

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

编写测试类:

public class JVMTest4 {

    private static class ObjectA {
        String str;  
        int i1; 
        byte b1; 
        byte b2; 
        int i2;  
        byte b3;  
    }

    public static void main(String[] args){
        System.out.println(ObjectSize.sizeOf(new ObjectA()));
    }
}

运行程序:

[图片上传失败...(image-c1c0a4-1525935829095)]

将打包好的jar文件,用解压缩工具打开,修改META-INF/MANIFEST.MF文件,告诉虚拟机在程序执行的时候执行ObjectSize类的permain方法,从名称也可以看出,含义为:“在main方法之前执行”。

编译运行

此步骤,是实际的运行过程,需要将上面的2个类进行编译,并且将ObjectSize打包,执行“java -javaagent:ObjectSize.jar JVMTest4”命令。

从截图中,我们可以看出ObjectA对象在内存中占用了32个字节。

上文中说了。对象的大小为8的倍数,如果不足8的倍数则会进行对齐填充。

下面,我们来手动计算下(64位机器,默认开启指针压缩)

原生类型 占用内存大小(字节)
boolean 1
byte 1
short 2
char 2
int 4
float 4
long 8
double 8
reference 开启指针压缩4、关闭指针压缩8

对象引用(reference)类型在64位机器上,关闭指针压缩时占用8字节, 开启时占用4字节。

实例数据:str(4)+i1(4)+b1(1)+b2(1)+i2(4)+b3(1) = 15字节

对象头:8(Mark Word) + 4(类型指针) = 12字节

对齐填充:5字节

总计:15 + 12 + 5 = 32字节

关闭指针压缩情况下:使用-XX:-UseCompressedOops命令

实例数据:str(8)+i1(4)+b1(1)+b2(1)+i2(4)+b3(1) = 19字节

对象头:8(Mark Word) + 8(类型指针) = 16字节

对齐填充:5字节

总计:19 + 16 + 5 = 40字节

数组对象:

Instrumentation注入类:

public class ObjectSize {

    private static Instrumentation inst;

    public static void premain(String agentArgs, Instrumentation instP){
        inst = instP;
    }

    public static long sizeOf(Object obj){
        return inst.getObjectSize(obj);
    }
}

编写测试类:

public class JVMTest4 {

    private static class ObjectA {
        String str;  
        int i1; 
        byte b1; 
        byte b2; 
    }

    private static class ObjectB {
        
    }

    public static void main(String[] args){
        System.out.println(ObjectSize.sizeOf(new ObjectA[0]));
        System.out.println(ObjectSize.sizeOf(new ObjectA[1]));
        System.out.println(ObjectSize.sizeOf(new ObjectA[2]));

        System.out.println(ObjectSize.sizeOf(new ObjectB[0]));
        System.out.println(ObjectSize.sizeOf(new ObjectB[1]));
        System.out.println(ObjectSize.sizeOf(new ObjectB[2]));
    }
}

运行程序:

image

从测试结果来看,数组对象要比普通对象占用内存空间更大。值得注意的是,数组占用内存的大小并不会根据成员变量的增加而增大。无论是否存在成员变量,都不会影响数组对象占用内存的大小。

你可能还有个疑惑?例子中的数组只设置了长度,而没有实际赋值对象,如果向对应的角标下赋值,数组对象占用内存的大小会有变化吗?

答案:NO!!

数组对象占用内存大小公式:

Mark Word + 类型指针 + 数组长度 + 实例数据(数组长度*数组元数据大小) +补齐填充

数组与普通对象不同之处,在其实例数据部分。对于普通对象来说,实例数据就是其内部的成员变量;而对于数组来说,实例数据就是其内部的一个个对象的指针,而对象指针所占用内存大小在开启指针压缩情况下为4字节,关闭指针压缩情况下为8字节。

开启指针压缩:

mark word 8  + 类型指针 4 + 数组长度 4 + 0*4 + 补齐 0  = 16

mark word 8  + 类型指针 4 + 数组长度 4 + 1*4 + 补齐 4  = 24

mark word 8  + 类型指针 4 + 数组长度 4 + 2*4 + 补齐 0  = 24

mark word 8  + 类型指针 4 + 数组长度 4 + 3*4 + 补齐 0  = 32

未开启指针压缩:

mark word 8  + 类型指针 8 + 数组长度 4 + 0*8 +  + 补齐 0  = 24

mark word 8  + 类型指针 8 + 数组长度 4 + 1*8 + 补齐 4  = 32

mark word 8  + 类型指针 8 + 数组长度 4 + 2*8 + 补齐 4  = 40

mark word 8  + 类型指针 8 + 数组长度 4 + 3*8 +  + 补齐 4  = 56

再次强调下:

我们例子中调用的getObjectSize()方法得到的是Shallow Size,即遇到引用时,只计算引用的长度,不计算所引用对象的实际大小。如果要计算所引用对象的实际大小,可以通过递归的方式去计算。本文暂不介绍此方式,有兴趣的朋友可以去网上查阅相关资料。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容