分享

计算Java Serializable对象大小的方法

 CevenCheng 2011-09-07

在Cahce的设计中,一般会考虑两种对象管理方法,一是按对象大小,比如开一个10M字节的cahce空间。另外一个是按对象的数量,比如1000个对象的cache空间。

 

  对于按对象尺寸管理的cache,由于Java对象的实际内存大小不好获得(我是不知道如何计算的),所以一般就使用一个Serializable对象的序列化尺寸来代替,序列化时通常把一个对象序列化到一个字节buffer里,那么就可以获得这个buffer的字节数。今天,在工作中,突软想到这样做太浪费空间,看了Java的ObjectOutputStream这个类的源码,发现也可以如下做,只计数,不产生字节转移,那么就不会耗费内存空间了。

Java代码  收藏代码
  1. public final class SizeCalculator {  
  2.   public static int calcSize(java.io.Serializable o) {  
  3.   int ret = 0;  
  4.   class DumbOutputStream extends OutputStream {  
  5.   int count = 0;  
  6.   public void write(int b) throws IOException {  
  7.   count++; // 只计数,不产生字节转移  
  8.   }  
  9.   }  
  10.   DumbOutputStream buf = new DumbOutputStream();  
  11.   ObjectOutputStream os = null;  
  12.   try {  
  13.   os = new ObjectOutputStream(buf);  
  14.   os.writeObject(o);  
  15.   ret = buf.count;  
  16.   } catch (IOException e) {  
  17.   // No need handle this exception  
  18.   e.printStackTrace();  
  19.   ret = -1;  
  20.   } finally {  
  21.   try {  
  22.   os.close();  
  23.   } catch (Exception e) {  
  24.   }  
  25.   }  
  26.   return ret;  
  27.   }  
  28.   public static void main(String[] args){  
  29.   System.err.println(calcSize(1));  
  30.   }  
  31.   }  

 

 

============================

Java对象在内存中的形态跟序列化后的形态占的空间大小没有必然联系诶。
用行为良好的Java代码来实现序列化,在任何符合JVM规范的环境中对同样的Java对象做序列化得到的都应该是一样的结果。然而在不同的JVM实现中,对象布局是不一样的,甚至在一些实现了automatic object inlining的JVM中对象布局还可以应需调整。
除了对象布局外,数据本身的保存方式也可能有出入。例如字符串,如果某个JVM内部是用UTF-16来保存而某中序列化方式采用UTF-8来保存,那么同一字符串占的空间就可能有差异。

比较靠谱的办法是通过JVMTI来获取Java对象大小。可以参考这篇文章里实现的一个JVMTI agent,它实现了单个对象的sizeOf()与某个对象及其所引用的所有对象的fullSizeOf()。
以前有一帖提到了在Sun的JRE 1.6.0 update 14的HotSpot VM中某些数组对象在内存中的布局,有兴趣的话也可以看看。
==========================
/** 
*Name: Sizeof.java 
*Copyright: zhaozhihua 

*/ 
package com.boco.gdau.util; 

/** 
* Description: 
* <p>Change Log: 
* <p>2010-2-4 create by zhaozhihua 
* <p> 
* @version 1.0 
* @date 2010-2-4 
* @author zhaozhihua 
*/ 
import java.io.PrintStream; 
import java.lang.reflect.Array; 
import java.lang.reflect.Field; 
import java.lang.reflect.Modifier; 
import java.util.Enumeration; 
import java.util.HashMap; 
import java.util.Hashtable; 
import java.util.IdentityHashMap; 
import java.util.Iterator; 
import java.util.Map; 
import java.util.Stack; 

/*+********************************************************************* 
This program is free software; you can redistribute it and/or 
modify it under the terms of the GNU General Public License 
as published by the Free Software Foundation; either version 2 
of the License, or (at your option) any later version. 

This program is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
GNU General Public License for more details. 

You should have received a copy of the GNU General Public License 
along with this program; if not, write to the Free Software Foundation 
Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 
************************************************************************/ 



/** 
* <p>estimator for the size of data structures. The sizes of 
* individual elements used to compute the overall size of a data 
* structure are estimates only derived on 1.4.2 on a 32bit Linux.</p> 

* <p>The implementation will be changed to 
* <code>java.lang.instrument.Instrumentation</code> as soon as we 
* switch to Java 5.</p> 

* @author &copy; 2005 Harald Kirsch 
*/ 
public class Sizeof { 

  // no need for this to show up in the docs 
  private Sizeof() {} 

  static final Hashtable pSizes; 
  static { 
    pSizes = new Hashtable(); 
    pSizes.put(Boolean.TYPE, new Integer(1)); 
    pSizes.put(Character.TYPE, new Integer(2)); 
    pSizes.put(Byte.TYPE, new Integer(1)); 
    pSizes.put(Short.TYPE, new Integer(2)); 
    pSizes.put(Integer.TYPE, new Integer(4)); 
    pSizes.put(Long.TYPE, new Integer(8)); 
    pSizes.put(Float.TYPE, new Integer(4)); 
    pSizes.put(Double.TYPE, new Integer(8)); 
    pSizes.put(Void.TYPE, new Integer(1)); 
  } 
  private static final class Root {} 

  private static void sizeof(Object obj, IdentityHashMap known, 
                             Hashtable types) 
  { 
    // The stack always contains a parent class and an object. The 
    // parent class may be null 
    Stack stack = new Stack(); 
    stack.push(new Root().getClass()); 
    stack.push(obj); 
    int rounds = 0; 
    while( !stack.empty() ) { 
      Object o = stack.pop(); 
      Object parentClass = stack.pop(); 
      known.put(o, o); 
      rounds += 1; 
      if( rounds%100000==0 ) { 
        System.err.println("rounds: "+rounds+", stacksize:"+stack.size()); 
      } 
      int size = 8; 
      Class oc = o.getClass(); 

      // arrays need totally different handling 
      if( oc.isArray() ) { 
        // arrays seem to have a minimum size of 16 
        size += 8; 
        int alen = Array.getLength(o); 
        Class ec = oc.getComponentType(); 
        Integer l = (Integer)pSizes.get(ec); 
        if( l!=null ) { 
          // array of primitive types 
          size = size + alen*l.intValue(); 
        } else { 
          size = size + 4*alen; 
          Object[] oa = (Object[])o; 
          for(int i=0; i<alen; i++) { 
            Object child = oa[i]; 
            if( child!=null && !known.containsKey(child) ) { 
              // don't use arrays as parents (does not tell a lot) 
              stack.push(parentClass); 
              stack.push(child); 
            } 
          } 
        } 

      } else { 
        // handle o not being an array 

        // iterate up the inheritance hierarchy 
        Class c = oc; 
        while( c!=null ) { 
          Field[] fields = c.getDeclaredFields(); 
          for(int i=0; i<fields.length; i++) { 
            Field f = fields[i]; 
            if( Modifier.isStatic(f.getModifiers()) ) continue; 

            Class fc = f.getType(); 

            // take care of primitive types 
            // FIX ME: this disregards alignment issues 
            Integer l = (Integer)pSizes.get(fc); 
            if( l!=null ) { 
              size+=l.longValue(); 
              continue; 
            } 

            size+=4; 
            Object child; 
            f.setAccessible(true); 
            try { 
              child = f.get(o); 
              if( child==null || known.containsKey(child) ) continue; 
              stack.push(oc);   // should this rather be c? 
              stack.push(child); 
              //System.out.println(""+fc+" in "+c); 
            } catch( IllegalAccessException e ) { 
              System.err.println("cannot access "+f+" in "+oc); 
            } 

          } 
          c = c.getSuperclass(); 
        } 

      } 

      Map parents = (Map)types.get(oc); 
      if( parents==null ) types.put(oc, parents=new HashMap()); 
      Pair p = (Pair)parents.get(parentClass); 
      if( p==null ) { 
        p = new Pair(); 
        parents.put(parentClass, p); 
      } 
      p.count +=1 ; 
      p.size += size; 
    }//while stack not empty 
  } 
  /**********************************************************************/ 
  /** 
   * recursively delves into the object graph <code>o</code> and 
   * returns a <code>Hashtable</code> with information about object 
   * sizes and counts. The keys used in the result are 
   * <code>java.lang.Class</code> objects. 
   * Each key <em>k</em> is mapped to a <code>java.util.Map</code> 
   * object which pairs a class object <em>r</em> and a {@link 
   * Sizeof.Pair}. The <code>Pair</code> describes how often 
   * <em>k</em> appears as a field in an object of type <em>r</em>. In 
   * other words: within the object graph, objects of type <em>r</em> 
   * are parents of objects of type <em>k</em>.</p> 
   */ 
  public static Hashtable sizeof(Object o) { 
    Hashtable types = new Hashtable(); 
    sizeof(o, new IdentityHashMap(), types); 
    return types; 
  } 
  /** 
   * prints the result produced with {@link #sizeof sizeof()}. 
   */ 
  public static void printTypes(PrintStream out, Hashtable types) { 
    Enumeration it = types.keys(); 
    long count=0, size=0; 
    while( it.hasMoreElements() ) { 
      Class c = (Class)it.nextElement(); 
      Map parents = (Map)types.get(c); 

      Iterator pit = parents.keySet().iterator(); 
      while( pit.hasNext() ) { 
        Class parenClass = (Class)pit.next(); 
        Pair p = (Pair)parents.get(parenClass); 
        String pname = parenClass.getName(); 
        out.println(""+p.count+" "+p.size+" "+c.getName()+" "+pname); 
        count += p.count; 
        size += p.size; 
      } 
    } 
    out.println(""+count+" "+size+ " TOTAL"); 
  } 
  /**********************************************************************/ 
  /** 
   * a pair of count and size describing use and size of a data type. 
   */ 
  public static class Pair { 
    /** 
     * number of objects of a certain type. 
     */ 
    public int count; 
    /** 
     * accumulated size of objects counted. 
     */ 
    public long size; 
  } 
   public static void main(String[] argv){ 
     
     int o=1; 
     Hashtable types = sizeof(o); 
     printTypes(System.out, types); 
   } 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多