一个对象占用多少字节?

标签: 对象 字节 | 发表时间:2016-11-13 15:30 | 作者:jaesonchen
出处:http://www.iteye.com

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

Java代码   收藏代码
  1. import java.lang.instrument.Instrumentation;  
  2. import java.lang.reflect.Array;  
  3. import java.lang.reflect.Field;  
  4. import java.lang.reflect.Modifier;  
  5. import java.util.ArrayDeque;  
  6. import java.util.Deque;  
  7. import java.util.HashSet;  
  8. import java.util.Set;  
  9.   
  10. /** 
  11.  * 对象占用字节大小工具类 
  12.  * 
  13.  * @author tianmai.fh 
  14.  * @date 2014-03-18 11:29 
  15.  */  
  16. public class SizeOfObject {  
  17.     static Instrumentation inst;  
  18.   
  19.     public static void premain(String args, Instrumentation instP) {  
  20.         inst = instP;  
  21.     }  
  22.   
  23.     /** 
  24.      * 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br></br> 
  25.      * 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br></br> 
  26.      * 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br></br> 
  27.      * 
  28.      * @param obj 
  29.      * @return 
  30.      */  
  31.     public static long sizeOf(Object obj) {  
  32.         return inst.getObjectSize(obj);  
  33.     }  
  34.   
  35.     /** 
  36.      * 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小 
  37.      * 
  38.      * @param objP 
  39.      * @return 
  40.      * @throws IllegalAccessException 
  41.      */  
  42.     public static long fullSizeOf(Object objP) throws IllegalAccessException {  
  43.         Set<Object> visited = new HashSet<Object>();  
  44.         Deque<Object> toBeQueue = new ArrayDeque<>();  
  45.         toBeQueue.add(objP);  
  46.         long size = 0L;  
  47.         while (toBeQueue.size() > 0) {  
  48.             Object obj = toBeQueue.poll();  
  49.             //sizeOf的时候已经计基本类型和引用的长度,包括数组  
  50.             size += skipObject(visited, obj) ? 0L : sizeOf(obj);  
  51.             Class<?> tmpObjClass = obj.getClass();  
  52.             if (tmpObjClass.isArray()) {  
  53.                 //[I , [F 基本类型名字长度是2  
  54.                 if (tmpObjClass.getName().length() > 2) {  
  55.                     for (int i = 0, len = Array.getLength(obj); i < len; i++) {  
  56.                         Object tmp = Array.get(obj, i);  
  57.                         if (tmp != null) {  
  58.                             //非基本类型需要深度遍历其对象  
  59.                             toBeQueue.add(Array.get(obj, i));  
  60.                         }  
  61.                     }  
  62.                 }  
  63.             } else {  
  64.                 while (tmpObjClass != null) {  
  65.                     Field[] fields = tmpObjClass.getDeclaredFields();  
  66.                     for (Field field : fields) {  
  67.                         if (Modifier.isStatic(field.getModifiers())   //静态不计  
  68.                                 || field.getType().isPrimitive()) {    //基本类型不重复计  
  69.                             continue;  
  70.                         }  
  71.   
  72.                         field.setAccessible(true);  
  73.                         Object fieldValue = field.get(obj);  
  74.                         if (fieldValue == null) {  
  75.                             continue;  
  76.                         }  
  77.                         toBeQueue.add(fieldValue);  
  78.                     }  
  79.                     tmpObjClass = tmpObjClass.getSuperclass();  
  80.                 }  
  81.             }  
  82.         }  
  83.         return size;  
  84.     }  
  85.   
  86.     /** 
  87.      * String.intern的对象不计;计算过的不计,也避免死循环 
  88.      * 
  89.      * @param visited 
  90.      * @param obj 
  91.      * @return 
  92.      */  
  93.     static boolean skipObject(Set<Object> visited, Object obj) {  
  94.         if (obj instanceof String && obj == ((String) obj).intern()) {  
  95.             return true;  
  96.         }  
  97.         return visited.contains(obj);  
  98.     }  
  99. }  

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

Java代码   收藏代码
  1. Premain-class: xxx.yyy.zzz.SizeOfObject  
  2. Can-Redefine-Classes: false  
  3. Boot-Class-Path:   

        来看看测试类:

Java代码   收藏代码
  1. import java.io.File;  
  2. import static com.tmall.buy.structure.SizeOfObject.*;  
  3. /** 
  4.  * @author tianmai.fh 
  5.  * @date 2014-03-18 20:17 
  6.  */  
  7. public class SizeOfObjectTest {  
  8.     /** 
  9.      * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 = 16 
  10.      * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + padding/4 = 24 
  11.      */  
  12.     static class A {  
  13.         int a;  
  14.     }  
  15.   
  16.     /** 
  17.      * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24 
  18.      * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + 4 = 24 
  19.      */  
  20.     static class B {  
  21.         int a;  
  22.         int b;  
  23.     }  
  24.   
  25.     /** 
  26.      * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24 
  27.      * -XX:-UseCompressedOops: mark/8 + metedata/8 + 8 + 4 + padding/4 = 32 
  28.      */  
  29.     static class B2 {  
  30.         int b2a;  
  31.         Integer b2b;  
  32.     }  
  33.   
  34.     /** 
  35.      * 不考虑对象头: 
  36.      * 4 + 4 + 4 * 3 + 3 * sizeOf(B) 
  37.      */  
  38.     static class C extends A {  
  39.         int ba;  
  40.         B[] as = new B[3];  
  41.   
  42.         C() {  
  43.             for (int i = 0; i < as.length; i++) {  
  44.                 as[i] = new B();  
  45.             }  
  46.         }  
  47.     }  
  48.   
  49.     static class D extends B {  
  50.         int da;  
  51.         Integer[] di = new Integer[3];  
  52.     }  
  53.   
  54.     /** 
  55.      * 会算上A的实例字段 
  56.      */  
  57.     static class E extends A {  
  58.         int ea;  
  59.         int eb;  
  60.     }  
  61.   
  62.     public static void main(String[] args) throws IllegalAccessException {  
  63.         System.out.println(new File("./target/classes").getAbsolutePath());  
  64.         System.out.println("sizeOf(new Object())=" + sizeOf(new Object()));  
  65.         System.out.println("sizeOf(new A())=" + sizeOf(new A()));  
  66.         System.out.println("sizeOf(new B())=" + sizeOf(new B()));  
  67.         System.out.println("sizeOf(new B2())=" + sizeOf(new B2()));  
  68.         System.out.println("sizeOf(new B[3])=" + sizeOf(new B[3]));  
  69.         System.out.println("sizeOf(new C())=" + sizeOf(new C()));  
  70.         System.out.println("fullSizeOf(new C())=" + fullSizeOf(new C()));  
  71.         System.out.println("sizeOf(new D())=" + sizeOf(new D()));  
  72.         System.out.println("fullSizeOf(new D())=" + fullSizeOf(new D()));  
  73.         System.out.println("sizeOf(new int[3])=" + sizeOf(new int[3]));  
  74.         System.out.println("sizeOf(new Integer(1)=" + sizeOf(new Integer(1)));  
  75.         System.out.println("sizeOf(new Integer[0])=" + sizeOf(new Integer[0]));  
  76.         System.out.println("sizeOf(new Integer[1])=" + sizeOf(new Integer[1]));  
  77.         System.out.println("sizeOf(new Integer[2])=" + sizeOf(new Integer[2]));  
  78.         System.out.println("sizeOf(new Integer[3])=" + sizeOf(new Integer[3]));  
  79.         System.out.println("sizeOf(new Integer[4])=" + sizeOf(new Integer[4]));  
  80.         System.out.println("sizeOf(new A[3])=" + sizeOf(new A[3]));  
  81.         System.out.println("sizeOf(new E())=" + sizeOf(new E()));  
  82.     }  
  83. }  

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

Java代码   收藏代码
  1. -javaagent:target/test.jar   

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

Java代码   收藏代码
  1. sizeOf(new Object())=16  
  2. sizeOf(new A())=16  
  3. sizeOf(new B())=24  
  4. sizeOf(new B2())=24  
  5. sizeOf(new B[3])=32  
  6. sizeOf(new C())=24  
  7. fullSizeOf(new C())=128  
  8. sizeOf(new D())=32  
  9. fullSizeOf(new D())=64  
  10. sizeOf(new int[3])=32  
  11. sizeOf(new Integer(1)=16  
  12. sizeOf(new Integer[0])=16  
  13. sizeOf(new Integer[1])=24  
  14. sizeOf(new Integer[2])=24  
  15. sizeOf(new Integer[3])=32  
  16. sizeOf(new Integer[4])=32  
  17. sizeOf(new A[3])=32  
  18. sizeOf(new E())=24  

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

Java代码   收藏代码
  1. sizeOf(new Object())=16  
  2. sizeOf(new A())=24  
  3. sizeOf(new B())=24  
  4. sizeOf(new B2())=32  
  5. sizeOf(new B[3])=48  
  6. sizeOf(new C())=40  
  7. fullSizeOf(new C())=160  
  8. sizeOf(new D())=40  
  9. fullSizeOf(new D())=88  
  10. sizeOf(new int[3])=40  
  11. sizeOf(new Integer(1)=24  
  12. sizeOf(new Integer[0])=24  
  13. sizeOf(new Integer[1])=32  
  14. sizeOf(new Integer[2])=40  
  15. sizeOf(new Integer[3])=48  
  16. sizeOf(new Integer[4])=56  
  17. sizeOf(new A[3])=48  
  18. 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个字节,这是又是为什么呢?



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [对象 字节] 推荐:

一个对象占用多少字节?

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

JAVA内存使用--如何计算一个Java对象占用的字节数

- - Java - 编程语言 - ITeye博客
转自(http://blog.csdn.net/kp034/article/details/7077757). 通常,我们谈论的堆内存使用的前提是以“一般情况”为背景的. 1.某些情况下,JVM根本就没有把Object放入堆中. 例如:原则上讲,一个小的thread-local对象存在于栈中,而不是在堆中.

javascript对象转json

- - JavaScript - Web前端 - ITeye博客
把javascript对象转成json. 已有 0 人发表留言,猛击->> 这里<<-参与讨论. —软件人才免语言低担保 赴美带薪读研.

对象的消息模型

- loudly - 酷壳 - CoolShell.cn
[ ———— 感谢 Todd 同学 投递本文,原文链接 ———— ]. 话题从下面这段C++程序说起,你认为它可以顺利执行吗. 试试的确可以顺利运行输出hello world,奇怪吗. 其实并不奇怪,根据C++对象模型,类的非虚方法并不会存在于对象内存布局中,实际上编译器是把Hello方法转化成了类似这样的全局函数:.

jQuery的deferred对象详解

- 郑小东 - 阮一峰的网络日志
jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本. 今天我想介绍的,就是从jQuery 1.5.0版本开始引入的一个新功能----deferred对象. 这个功能很重要,未来将成为jQuery的核心方法,它彻底改变了如何在jQuery中使用ajax. 为了实现它,jQuery的全部ajax代码都被改写了.

我连对象都没有。。。

- 老五 - Lzhi&#39;s Views
两个黄鹂鸣翠柳,我连对象都没有. 劝君更尽一杯酒,我连对象都没有. 莫愁前路无知己,我连对象都没有. 借问酒家何处有,我连对象都没有. 停车坐爱枫林晚,我连对象都没有. 一枝红杏出墙来,我连对象都没有. 壮士一去不复还,我连对象都没有. 烈火焚烧浑不怕,我连对象都没有. 雌雄双兔奔地走,我连对象都没有.

js对象深拷贝

- - ITeye博客
在做一个前台页面你的时候用到了一个自己写的List对象,在进行深拷贝的时候参考了网上的代码:. //对象扩展,tObj被扩展对象,sObj扩展对象. Object.extend(a,b);//a获得了b的所有属性. 我自己定义的list中没有定义constructor,所以执行sObj.constructor == Array会报错,我就修改为:.

Java的对象驻留

- - Java译站
Java会将源代码中的字符串常量存储到常量池中. 这不只是说它俩的值是一样的,而是说就是同一个字符串对象. 用Java的话来说就是a==b的结果是true. 然而这个只对字符串以及小的整型或者长整型有效. 其它的对象是不会被驻留的,也就是说如果你创建了两个对象而他们的值是相等的,但他们并不是同一个对象.

方案对象管理

- - CSDN博客数据库推荐文章
方案是数据库用户拥有的数据库对象的集合,方案对象是直接引用数据库的逻辑结构,对象包括表、索引、序列、视图、同义词等结构.  这一章大部分内容,我们在SQL的第九章创建和管理表及约束和第十章其它常用对象都已讲过,做一些补充. 堆表就是普通表,也叫堆组织表. 之所以这样叫,是因为他使用数据结构中堆的算法来组织表.

jquery获取对象大全

- - CSDN博客Web前端推荐文章
1、JQuery的核心的一些方法 . $("Element").length; ‘元素的个数,是个属性 . $("Element").size(); ’也是元素的个数,不过带括号是个方法 . $("Element").get(); ‘某个元素在页面中的集合,以数组的形式存储 . $("Element").get(index); ’功能和上面的相同,index表示第几个元素,数组的下标 .