`

一个对象占用多少字节?

阅读更多

        老早之前写过一篇博客,是关于一个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  
很不错,学习了

相关推荐

    基于matlab实现人工免疫算法的解决TSP问题的方法

    基于matlab实现人工免疫算法的解决TSP问题的方法,体现了免疫算法在进化计算过程中的抗原学习、记忆机制、浓度调节机制以及多样性抗体保持策略等优良特性.rar

    麦肯锡图表绘制培训.pptx

    麦肯锡图表绘制培训.pptx

    Java_Android的自由轻量级流媒体前端.zip

    Java_Android的自由轻量级流媒体前端

    node-v18.20.2-linux-arm64

    node-v18.20.2-linux-arm64node-v18.20.2-linux-arm64 node-v18.20.2-linux-arm64node-v18.20.2-linux-arm64 node-v18.20.2-linux-arm64node-v18.20.2-linux-arm64 node-v18.20.2-linux-arm64node-v18.20.2-linux-arm64 node-v18.20.2-linux-arm64node-v18.20.2-linux-arm64 node-v18.20.2-linux-arm64node-v18.20.2-linux-arm64 node-v18.20.2-linux-arm64node-v18.20.2-linux-arm64 node-v18.20.2-linux-arm64node-v18.20.2-linux-arm64 node-v18.20.2-linux-arm64node-v18.20.2-linux-arm64

    华为的OD(Organizational Development)

    华为的OD(Organizational Development)

    从左到右的流水灯.PWI

    毕设&课设&项目&实训- 【项目资源】: 包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。 包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】: 所有源码都经过严格测试,可以直接运行。 功能在确认正常工作后才上传。 【适用人群】: 适用于希望学习不同技术领域的小白或进阶学习者。 可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】: 项目具有较高的学习借鉴价值,也可直接拿来修改复刻。 对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】: 有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 鼓励下载和使用,并欢迎大家互相学习,共同进步。

    520表白html5爱心代码

    520表白html5爱心代码

    node-v10.18.1-headers.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    vscode-1.52.0.tar源码文件

    vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件 vscode-1.52.0.tar源码文件vscode-1.52.0.tar源码文件

    flowable-designer-5.22.0.zip

    flowable-designer-5.22.0.zip

    node-v0.10.47-headers.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v4.2.6-headers.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    netty-transport-4.1.38.Final.jar

    javaEE javaweb常用jar包 , 亲测可用,下载后导入到java工程中使用。

    基于CAPL仿真BMS

    使用CANOE,基于CAPL仿真BMS,通过Panel控制发送

    node-v4.8.7-headers.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    Guice发音为juice,是Google为Java 11及以上版本提供的轻量级依赖注入框架.zip

    Guice发音为juice,是Google为Java 11及以上版本提供的轻量级依赖注入框架

    kafka_2.10-0.8.2.1.jar

    javaee/javaweb常用jar包,亲测可用,导入到java工程中即可使用

    node-v10.9.0-headers.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    用于模拟二维单粒子跟踪 (SPT) 数据集的 Matlab 软件.zip

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    grpcio-1.12.0-cp35-cp35m-manylinux1_x86_64.whl

    Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

Global site tag (gtag.js) - Google Analytics