精确计算一个Java对象占用的内存大小

《java优化占用内存的方法 -- 雪的痕迹》一文中提到:

" java做的系统给人的印象是什么?占内存!说道这句话就会有N多人站出来为java辩护,并举出一堆的性能测试报告来证明这一点。其实从理论上来讲java做的系统并不比其他语言开发出来的系统更占用内存,那么为什么却有这么N多理由来证明它确实占内存呢?两个字,"陋习"。

 

那如何衡量对象占用的内存空间大小呢?有人可能会说简单的使用如下代码: 

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

然而这样做只能计算出堆中所有实例占用的内存大小,无法做到对一个对象的精确计算,

本人写了一段小代码计算对象的内存占用量,得出的结果和自己的电脑上内存的使用量一样,电脑配置是64位CPU,装32位XP操作系统,32位sun的虚拟机(参数-client),内存3G,不清楚程序是否通用。

 

算法简述:

1.值类型变量占用大小已知

2.引用类型变量占用大小4字节(还没有在64位JVM下测试过),如Object x = null;  这个x变量在32位jvm中测得占用4字节

3.对象大小与占用空间的关系:occupy = (size + 8 - 1) / 8 * 8;

4.递归的遍历一个对象内的所有成员变量,成员变量的成员变量的占用的内存大小,

 

(目前无法对包含private java.lang.reflect.Constructot类型的成员的对象计算大小,因为无法通过反射获得对象该字段的值)。

import java.lang.reflect.*;
import java.util.*;
 
//一个引用:4字节
//一个Object:8字节
//一个Integer:16字节 == (8 + 4+8-1) / 8 * 8
//一个int:4字节
//长度为0的数组大小:JRo64=24,  Sun32=12
 
//引用大小,如Object = null:                    JRo64=JRo32=4, Sun32=4, Sun64=8
//无成员的对象大小,如new Object();:     JRo32=JRo64=8, Sun32=8, Sun64=16
//new byte[0]:                        JRo32=JRo64=8+8 Sun32=8+4, Sun64=16+8
 
//长度l的byte数组:(l+12+8-1)/8*8
//长度l的char/short数组:(l*2+12+8-1)/8*8 == (l+9)/4*8
//长度l的String:(l+1)/4*8+40
//长度l的int数组:(l*4+12+8-1)/8*8 ==(l+4)/2*8
//长度l的long数组:(l*8+12+8-1)/8*8 == (l+2)*8
public class Occupy {
 
  //这8个方法不写不行,否则occupyof(int x)会自动重载到occupyof(Object o),并且无法在方法中判断
  public static int occupyof(boolean variable) {
    return 1;
  }
 
  public static int occupyof(byte variable) {
    return 1;
  }
 
  public static int occupyof(short variable) {
    return 2;
  }
 
  public static int occupyof(char variable) {
    return 2;
  }
 
  public static int occupyof(int variable) {
    return 4;
  }
 
  public static int occupyof(float variable) {
    return 4;
  }
 
  public static int occupyof(long variable) {
    return 8;
  }
 
  public static int occupyof(double variable) {
    return 8;
  }
 
  public Occupy(byte nullReferenceSize, byte emptyObjectSize, byte emptyArrayVarSize) {
    this.NULL_REFERENCE_SIZE = nullReferenceSize;
    this.EMPTY_OBJECT_SIZE = emptyObjectSize;
    this.EMPTY_ARRAY_VAR_SIZE = emptyArrayVarSize;
  }
 
  public static Occupy forJRockitVM() {
    return new Occupy((byte) 4, (byte) 8, (byte) 8);
  }
 
  public static Occupy forSun32BitsVM() {
    return new Occupy((byte) 4, (byte) 8, (byte) 4);
  }
 
  public static Occupy forSun64BitsVM() {
    return new Occupy((byte) 8, (byte) 16, (byte) 8);
  }
 
  public static Occupy forDetectedVM(){
    return null;
  }
 
  private final byte NULL_REFERENCE_SIZE;
  private final byte EMPTY_OBJECT_SIZE;
  private final byte EMPTY_ARRAY_VAR_SIZE;
 
  private static class ref{
    public ref(Object obj){
      this.obj = obj;
    }
    final Object obj;
    @Override
    public boolean equals(Object obj) {
      return (obj instanceof ref) && ((ref)obj).obj == this.obj;
    }
    @Override
    public int hashCode() {
      return obj.hashCode();
    }
  }
 
  private List dedup = new ArrayList();
  
  public int occupyof(Object object){
    dedup.clear();
    return occupyof0(object);
  }
  private int occupyof0(Object object) {
    if (object == null)
      return 0;
    ref r = new ref(object);
    if(dedup.contains(r))
      return 0;
    dedup.add(r);
    int varSize = 0;//对象中的值类型、引用类型变量大小
    int objSize = 0;//对象中的引用类型指向的对象实例的大小
    for (Class clazz = object.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
//      System.out.println(clazz);
      if (clazz.isArray()) {//当前对象的数组
        varSize += EMPTY_ARRAY_VAR_SIZE;
        Class<?> componentType = clazz.getComponentType();
        if (componentType.isPrimitive()) {//当前数组是原生类型的数组
          varSize += lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType);
          return occupyOfSize(EMPTY_OBJECT_SIZE, varSize, 0);
        }
        Object[] array = (Object[]) object;
        varSize += NULL_REFERENCE_SIZE * array.length;//当前数组有length个引用,每个占用4字节
        for (Object o : array)
          objSize += occupyof0(o);
        return occupyOfSize(EMPTY_OBJECT_SIZE, varSize, objSize);
      }
      Field[] fields = clazz.getDeclaredFields();
      for (Field field : fields) {
        if (Modifier.isStatic(field.getModifiers()))
          continue;//类成员不计
        //System.out.println(field.getDeclaringClass());
        if(clazz != field.getDeclaringClass())
          continue;
        Class<?> type = field.getType();
        if (type.isPrimitive())
          varSize += sizeofPrimitiveClass(type);
        else {
          varSize += NULL_REFERENCE_SIZE;//一个引用型变量占用4个字节
          try {
            field.setAccessible(true);//可以访问非public类型的变量
              objSize += occupyof0(field.get(object));
          } catch (Exception e) {
            objSize += occupyofConstructor(object, field);
          }
        }
      }
    }
    return occupyOfSize(EMPTY_OBJECT_SIZE, varSize, objSize);
  }
 
  public static int sizeof(boolean variable) {
    return 1;
  }
 
  public static int sizeof(byte variable) {
    return 1;
  }
 
  public static int sizeof(short variable) {
    return 2;
  }
 
  public static int sizeof(char variable) {
    return 2;
  }
 
  public static int sizeof(int variable) {
    return 4;
  }
 
  public static int sizeof(float variable) {
    return 4;
  }
 
  public static int sizeof(long variable) {
    return 8;
  }
 
  public static int sizeof(double variable) {
    return 8;
  }
 
  
  public int sizeof(Object object) {
    if (object == null)
      return 0;
    int size = EMPTY_OBJECT_SIZE;
    Class clazz = object.getClass();
    if (clazz.isArray()) {
      size += EMPTY_ARRAY_VAR_SIZE;//length变量是int型
      Class<?> componentType = clazz.getComponentType();
      if (componentType.isPrimitive())
        return size + lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType);
      Object[] array = (Object[]) object;
      size += 4 * array.length;
      for (Object o : array)
        size += sizeof(o);
      return size;
    }
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
      if (Modifier.isStatic(field.getModifiers()))
        continue;//类成员不计
      Class<?> type = field.getType();
      if (type.isPrimitive())
        size += sizeofPrimitiveClass(type);
      else {
        size += 4;//一个引用型变量占用4个字节
        try {
          field.setAccessible(true);//可以访问非public类型的变量
          size += sizeof(field.get(object));
        } catch (Exception e) {
          size += sizeofConstructor(object, field);
        }
      }
    }
    return size;
  }
 
  private static int occupyofConstructor(Object object, Field field) {
    throw new UnsupportedOperationException("field type Constructor not accessible: " + object.getClass() + " field:" + field);
  }
 
  private static int sizeofConstructor(Object object, Field field) {
    throw new UnsupportedOperationException("field type Constructor not accessible: " + object.getClass() + " field:" + field);
  }
 
  
  private static int occupyOfSize(int size) {
    return (size + 7) / 8 * 8;
  }
 
  private static int occupyOfSize(int selfSize, int varsSize, int objsSize) {
//    System.out.println("self=" + selfSize + " vars=" + varsSize + " objs=" + objsSize);
    return occupyOfSize(selfSize) + occupyOfSize(varsSize) + objsSize;
  }
 
  private static int sizeofPrimitiveClass(Class clazz) {
    return clazz == boolean.class || clazz == byte.class ? 1 : clazz == char.class || clazz == short.class ? 2 : clazz == int.class || clazz == float.class ? 4
        : 8;
  }
 
  private static int lengthOfPrimitiveArray(Object object) {
    Class<?> clazz = object.getClass();
    return clazz == boolean[].class ? ((boolean[]) object).length : clazz == byte[].class ? ((byte[]) object).length
        : clazz == char[].class ? ((char[]) object).length : clazz == short[].class ? ((short[]) object).length
            : clazz == int[].class ? ((int[]) object).length : clazz == float[].class ? ((float[]) object).length
                : clazz == long[].class ? ((long[]) object).length : ((double[]) object).length;
  }
}

转自: http://blog.csdn.net/hoszb/article/details/5846069

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值