`

一个对象占用多少字节?

阅读更多

        老早之前写过一篇博客,是关于一个Integer对象到底占用多少字节的,现在看来,那篇文章竟然计算错了。这次再去计算,是因为之前写的一篇关于字长的文章里,看到了hotspot jvm里,对象占用空间是8字节对齐的,再加上之前关于字节那文章里带着一点-XX:+UseCompressedOops压缩指针参数的疑问,重新探究了下一个对象到底占用多少字节,以及如何计算它占用空间的方法。主要是参考了这篇很久以前的文章,不过试验了一把,instrumentation这种方法还是靠谱的。

import java.lang.instrument.Instrumentation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;

/**
 * 对象占用字节大小工具类
 *
 * @author tianmai.fh
 * @date 2014-03-18 11:29
 */
public class SizeOfObject {
    static Instrumentation inst;

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

    /**
     * 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br></br>
     * 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br></br>
     * 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br></br>
     *
     * @param obj
     * @return
     */
    public static long sizeOf(Object obj) {
        return inst.getObjectSize(obj);
    }

    /**
     * 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小
     *
     * @param objP
     * @return
     * @throws IllegalAccessException
     */
    public static long fullSizeOf(Object objP) throws IllegalAccessException {
        Set<Object> visited = new HashSet<Object>();
        Deque<Object> toBeQueue = new ArrayDeque<>();
        toBeQueue.add(objP);
        long size = 0L;
        while (toBeQueue.size() > 0) {
            Object obj = toBeQueue.poll();
            //sizeOf的时候已经计基本类型和引用的长度,包括数组
            size += skipObject(visited, obj) ? 0L : sizeOf(obj);
            Class<?> tmpObjClass = obj.getClass();
            if (tmpObjClass.isArray()) {
                //[I , [F 基本类型名字长度是2
                if (tmpObjClass.getName().length() > 2) {
                    for (int i = 0, len = Array.getLength(obj); i < len; i++) {
                        Object tmp = Array.get(obj, i);
                        if (tmp != null) {
                            //非基本类型需要深度遍历其对象
                            toBeQueue.add(Array.get(obj, i));
                        }
                    }
                }
            } else {
                while (tmpObjClass != null) {
                    Field[] fields = tmpObjClass.getDeclaredFields();
                    for (Field field : fields) {
                        if (Modifier.isStatic(field.getModifiers())   //静态不计
                                || field.getType().isPrimitive()) {    //基本类型不重复计
                            continue;
                        }

                        field.setAccessible(true);
                        Object fieldValue = field.get(obj);
                        if (fieldValue == null) {
                            continue;
                        }
                        toBeQueue.add(fieldValue);
                    }
                    tmpObjClass = tmpObjClass.getSuperclass();
                }
            }
        }
        return size;
    }

    /**
     * String.intern的对象不计;计算过的不计,也避免死循环
     *
     * @param visited
     * @param obj
     * @return
     */
    static boolean skipObject(Set<Object> visited, Object obj) {
        if (obj instanceof String && obj == ((String) obj).intern()) {
            return true;
        }
        return visited.contains(obj);
    }
}

       跑代码前,需要按照那篇很老的文章先打包,这样才能注入Instrumentation实例,打包时候需要在MANIFEST.MF中写入三项值(注意包路径名改成自己的包名):

Premain-class: xxx.yyy.zzz.SizeOfObject
Can-Redefine-Classes: false
Boot-Class-Path: 

        来看看测试类:

import java.io.File;
import static com.tmall.buy.structure.SizeOfObject.*;
/**
 * @author tianmai.fh
 * @date 2014-03-18 20:17
 */
public class SizeOfObjectTest {
    /**
     * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 = 16
     * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + padding/4 = 24
     */
    static class A {
        int a;
    }

    /**
     * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
     * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + 4 = 24
     */
    static class B {
        int a;
        int b;
    }

    /**
     * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
     * -XX:-UseCompressedOops: mark/8 + metedata/8 + 8 + 4 + padding/4 = 32
     */
    static class B2 {
        int b2a;
        Integer b2b;
    }

    /**
     * 不考虑对象头:
     * 4 + 4 + 4 * 3 + 3 * sizeOf(B)
     */
    static class C extends A {
        int ba;
        B[] as = new B[3];

        C() {
            for (int i = 0; i < as.length; i++) {
                as[i] = new B();
            }
        }
    }

    static class D extends B {
        int da;
        Integer[] di = new Integer[3];
    }

    /**
     * 会算上A的实例字段
     */
    static class E extends A {
        int ea;
        int eb;
    }

    public static void main(String[] args) throws IllegalAccessException {
        System.out.println(new File("./target/classes").getAbsolutePath());
        System.out.println("sizeOf(new Object())=" + sizeOf(new Object()));
        System.out.println("sizeOf(new A())=" + sizeOf(new A()));
        System.out.println("sizeOf(new B())=" + sizeOf(new B()));
        System.out.println("sizeOf(new B2())=" + sizeOf(new B2()));
        System.out.println("sizeOf(new B[3])=" + sizeOf(new B[3]));
        System.out.println("sizeOf(new C())=" + sizeOf(new C()));
        System.out.println("fullSizeOf(new C())=" + fullSizeOf(new C()));
        System.out.println("sizeOf(new D())=" + sizeOf(new D()));
        System.out.println("fullSizeOf(new D())=" + fullSizeOf(new D()));
        System.out.println("sizeOf(new int[3])=" + sizeOf(new int[3]));
        System.out.println("sizeOf(new Integer(1)=" + sizeOf(new Integer(1)));
        System.out.println("sizeOf(new Integer[0])=" + sizeOf(new Integer[0]));
        System.out.println("sizeOf(new Integer[1])=" + sizeOf(new Integer[1]));
        System.out.println("sizeOf(new Integer[2])=" + sizeOf(new Integer[2]));
        System.out.println("sizeOf(new Integer[3])=" + sizeOf(new Integer[3]));
        System.out.println("sizeOf(new Integer[4])=" + sizeOf(new Integer[4]));
        System.out.println("sizeOf(new A[3])=" + sizeOf(new A[3]));
        System.out.println("sizeOf(new E())=" + sizeOf(new E()));
    }
}

         如果你是用maven打包的话,可以考虑在pom.xml文件中配置。打完jar包后,可以直接运行SizeOfObject了,但是要加上vm启动参数(test.jar是刚才打的jar包):

-javaagent:target/test.jar 

         在我64bit mac上,跑64位hotspot vm的结果如下,其中压缩对象指针参数是开启的,即-XX:+UseCompressedOops

sizeOf(new Object())=16
sizeOf(new A())=16
sizeOf(new B())=24
sizeOf(new B2())=24
sizeOf(new B[3])=32
sizeOf(new C())=24
fullSizeOf(new C())=128
sizeOf(new D())=32
fullSizeOf(new D())=64
sizeOf(new int[3])=32
sizeOf(new Integer(1)=16
sizeOf(new Integer[0])=16
sizeOf(new Integer[1])=24
sizeOf(new Integer[2])=24
sizeOf(new Integer[3])=32
sizeOf(new Integer[4])=32
sizeOf(new A[3])=32
sizeOf(new E())=24

         如果关闭指针压缩,即在vm启动参数中加上-XX:-UseCompressedOops结果会不一样:

sizeOf(new Object())=16
sizeOf(new A())=24
sizeOf(new B())=24
sizeOf(new B2())=32
sizeOf(new B[3])=48
sizeOf(new C())=40
fullSizeOf(new C())=160
sizeOf(new D())=40
fullSizeOf(new D())=88
sizeOf(new int[3])=40
sizeOf(new Integer(1)=24
sizeOf(new Integer[0])=24
sizeOf(new Integer[1])=32
sizeOf(new Integer[2])=40
sizeOf(new Integer[3])=48
sizeOf(new Integer[4])=56
sizeOf(new A[3])=48
sizeOf(new E())=32

         UseCompressOops开启和关闭,对对象头大小是有影响的,开启压缩,对象头是4+8=12byte;关闭压缩,对象头是8+8=16bytes。这个如何观察验证呢?

        基于上述事实,通过new A()和new B()占用字节推断,基本类型int在开启、关闭压缩情况下都是占用4个bytes的,这个没有影响。而通过B和B2在开启、关闭指针压缩情况下的对比看,Integer类型分别占了4 bytes和8 bytes,实际上引用类型都是这样。如何验证?

         new Integer[0]在压缩前后分别占用16、24个字节,这是又是为什么呢?

        欲知后事,且听下回分解!enjoy it !

分享到:
评论
4 楼 surpassno 2017-08-18  
很不错,学习了
3 楼 yueyemaitian 2015-03-17  
walle1027 写道
学习了,正式想要的

2 楼 walle1027 2015-03-16  
学习了,正式想要的
1 楼 叼烟斗的纤夫 2015-02-09  
很不错,学习了

相关推荐

    计算一个Java对象占用字节数的方法

    主要介绍了计算一个Java对象占用字节数的方法,较为详细的分析了Java中各类对象所占用的字节数,需要的朋友可以参考下

    Java 对象(数组)占多大空间(几个字节) 手把手做实验

    废话不多说,一起开干 1 前置知识 本次实验基于jdk8 64位以及以上版本。本机环境为jdk11 先查看一下jvm启动的默认参数,里面有...UseCompressedOops:普通对象指针压缩(oop是ordinary object pointer的缩写), UseComp

    一个java对象占多少个字节的总结和理解_javajvm_

    详细解释内存区域

    查看当前数据库中每个表所占字节(空间)大小

    查看当前数据库中每个表所占字节(空间)大小

    chilimyan#iOS-source-note#一个 NSObject 对象占用多少内存空间1

    //获得NSObject 类的实例对象的大小//获取obj对象指针获取的大小输出结果分别是8 和 16系统分配了16个字节给NSObject对象(通过mallo

    9计算机应用基础.doc

    期末作业考核 《计算机应用基础》 满分 100分 一、判断对错(每小题1...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 答:每个汉字占8个字节,32*32*100/8=12800,共占用12800字节. 2.将二进制数1001

    jsp内置对象的用法

    config对象是在一个Servlet初始化时,JSP引擎向它传递信息用的,此信息包括Servlet初始化时所要用到的参数(通过属性名和属性值构成)以及服务器的有关信息(通过传递一个ServletContext对象) 序号 方 法 说 明 ...

    18计算机应用基础.doc

    期末作业考核 《计算机应用基础》 满分 100...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 解:32*32=1024字节 每个汉字为8个字节所以1024÷8=128字节 将二进制数100101转换对应的十进制数和对应的

    6计算机应用基础.docx

    期末作业考核 《计算机应用基础》 满分 100分 一、判断对错(每小题1...最早设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 答:32x32的一个汉字就是32*32/8=128字节 100个汉字就是128*100=12800字节

    1计算机应用基础答案.doc

    期末作业考核 《计算机应用基础》 满分 100分 一、...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 答:汉字点阵当中每个点用1bit表示,8bit是一个字节 每个汉字点阵共32×32=1024个点,需要占用

    10计算机应用基础.doc

    期末作业考核 《计算机应用基础》 满分 100分 ...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 解:32×32÷8×100=12800 100个汉字的字形码信息需占用12800字节。 2.将二进制数100101转换对应的十

    1计算机应用基础.doc

    期末作业考核 《计算机应用基础》 满分 100分 ...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 解:32×32÷8×100=12800 100个汉字的字形码信息需占用12800字节 2.将二进制数100101转换对应的十进

    PHP 面向对象技术(全面讲解).txt

    一个项目要用到多少个类,用多少个对象,在那要定义类,定义 一个什么样的类,这个类实例化出多少个对象,类里面有多少个属性,有多少个方法等等,这就需 要读者通过在实际的开发中就实际问题分析设计和总结了。 类的...

    2018年春季《计算机应用基础》.doc

    期末作业考核 《计算机应用基础》 满分 100分 ...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 解:32×32÷8×100=12800 2.将二进制数100101转换对应的十进制数和对应的十六进制数。 解:100101 =

    40计算机应用基础.doc

    期末作业考核 《计算机应用基础》 满分 100分 一、判断对错...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 32×32÷8×100=12800 2.将二进制数100101转换对应的十进制数和对应的十六进制数。 10

    17计算机应用基础.doc

    期末作业考核 《计算机应用基础》 满分 100分 一、判断对错...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 解:32×32÷8×100=12800 2.将二进制数100101转换对应的十进制数和对应的十六进制数。

    结构体中的冒号

    有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为...

    4计算机应用基础.doc

    期末作业考核 《计算机应用...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 2.将二进制数100101转换对应的十进制数和对应的十六进制数。 三、简答题(每小题8分,共40分) 1.计算机由哪五部分组

    26计算机应用基础.doc

    期末作业考核 《计算机应用基础》 满分 100分 ...设汉字点阵为32×32,那么100个汉字的字形码信息需占用多少字节? 解: 32×32÷8×100=12800 2.将二进制数100101转换对应的十进制数和对应的十六进制数。 二进制:1 0

    超级有影响力霸气的Java面试题大全文档

    面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性:  多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化...

Global site tag (gtag.js) - Google Analytics