Package railo.commons.lang

Source Code of railo.commons.lang.SizeOf

package railo.commons.lang;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import railo.commons.io.SystemUtil;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageRuntimeException;
import railo.runtime.op.Caster;
import railo.runtime.type.Sizeable;

/**
* Calculation of object size.
*/
public class SizeOf {
  public static final int OBJECT_GRANULARITY_IN_BYTES = 8;
  public static final int WORD_SIZE = Architecture.getVMArchitecture().getWordSize();
    public static final int HEADER_SIZE = 2 * WORD_SIZE;

    public static final int DOUBLE_SIZE = 8;
    public static final int FLOAT_SIZE = 4;
    public static final int LONG_SIZE = 8;
    public static final int INT_SIZE = 4;
    public static final int SHORT_SIZE = 2;
    public static final int BYTE_SIZE = 1;
    public static final int BOOLEAN_SIZE = 1;
    public static final int CHAR_SIZE = 2;
    public static final int REF_SIZE = WORD_SIZE;

  private static ThreadLocal _inside=new ThreadLocal();
  private static ThreadLocal map=new ThreadLocal();


  private static ThreadLocal<Set<Integer>> done=new ThreadLocal<Set<Integer>>();
 

  private static boolean inside(boolean inside) {
    Boolean was=(Boolean) _inside.get();
    _inside.set(inside?Boolean.TRUE:Boolean.FALSE);
    return was!=null && was.booleanValue();
  }
 
    private static Map get(boolean clear) {
    Map m=(Map) map.get();
    if(m==null){
      m=new IdentityHashMap();
      map.set(m);
    }
    else if(clear) m.clear();
   
      return m;
  }
   
   
    /**
     * Calculates the size of an object.
     * @param object the object that we want to have the size calculated.
     * @return the size of the object or 0 if null.
     */

    public static long size2(Object o) {
      Set<Integer> d = done.get();
      boolean inside=true;
      if(d==null){
        inside=false;
        d=new HashSet<Integer>();
        done.set(d);
      }
      try{
        return size(o,d);
      }
      finally{
        if(!inside) done.set(null);
      }
    }
   
    private static long size(Object o,Set<Integer> done) {
      if(o == null) return 0;
      if(done.contains(o.hashCode())) return 0;
      done.add(o.hashCode());
     
      if(o instanceof Sizeable){
        return ((Sizeable)o).sizeOf();
      }
      Class clazz = o.getClass();
      long size=0;
     
      // Native ARRAY
      // TODO how big is the array itself
      if (clazz.isArray()) {
        Class ct = clazz.getComponentType();
       
        if(ct.isPrimitive())return primSize(ct)*Array.getLength(o);
       
        size=REF_SIZE*Array.getLength(o);
        for (int i=Array.getLength(o)-1; i>=0;i--) {
                size += size(Array.get(o, i),done);
            }
          return size;
        }
     
      if(o instanceof Boolean) return REF_SIZE+BOOLEAN_SIZE;
      if(o instanceof Character) return REF_SIZE+CHAR_SIZE;
     
      if(o instanceof Number){
        if(o instanceof Double || o instanceof Long) return REF_SIZE+LONG_SIZE;
        if(o instanceof Byte) return REF_SIZE+BYTE_SIZE;
        if(o instanceof Short) return REF_SIZE+SHORT_SIZE;
        return REF_SIZE+INT_SIZE;// float,int
      }
      if(o instanceof String){
        int len=((String)o).length();
        return (REF_SIZE*len)+(REF_SIZE*CHAR_SIZE);
      }
     
     
      throw new PageRuntimeException(new ExpressionException("can not terminate the size of a object of type ["+Caster.toTypeName(o)+":"+o.getClass().getName()+"]"));
    }
   
   
    private static int primSize(Class ct) {
      if(ct==double.class) return LONG_SIZE;
      if(ct==long.class) return LONG_SIZE;
      if(ct==float.class) return INT_SIZE;
      if(ct==short.class) return SHORT_SIZE;
      if(ct==int.class) return INT_SIZE;
      if(ct==byte.class) return BYTE_SIZE;
      if(ct==boolean.class) return BOOLEAN_SIZE;
      return CHAR_SIZE;
  }

  public static long size(Object object) {
      return size(object, Integer.MAX_VALUE);
    }
 
  public static long size(Object object,int maxDepth) {
      if (object==null)return 0;
      boolean wasInside=inside(true);
      Map instances=get(!wasInside);
      //IdentityHashMap instances = new IdentityHashMap();
        Map dictionary = new HashMap();
        long size = _size(object, instances, dictionary, maxDepth, Long.MAX_VALUE);
        inside(wasInside);
        return size;
    }


  private static long _size(Object object, Map instances,Map dictionary, int maxDepth,long maxSize) {
    try  {
    return __size(object, instances, dictionary, maxDepth, maxSize);
    }
    catch(Throwable t){
      t.printStackTrace();
      return 0;
    }
  }

  private static long __size(Object object, Map instances,Map dictionary, int maxDepth,long maxSize) {
        if (object==null || instances.containsKey(object) || maxDepth==0 || maxSize < 0) return 0;
       
        instances.put(object, object);
       
        if(object instanceof Sizeable)return ((Sizeable)object).sizeOf();
       
        if(object instanceof String){
          return (SizeOf.CHAR_SIZE*((String)object).length())+SizeOf.REF_SIZE;
        }
       
        if(object instanceof Number){
          if(object instanceof Double) return SizeOf.DOUBLE_SIZE+SizeOf.REF_SIZE;
          if(object instanceof Float) return SizeOf.FLOAT_SIZE+SizeOf.REF_SIZE;
          if(object instanceof Long) return SizeOf.LONG_SIZE+SizeOf.REF_SIZE;
          if(object instanceof Integer) return SizeOf.INT_SIZE+SizeOf.REF_SIZE;
          if(object instanceof Short) return SizeOf.SHORT_SIZE+SizeOf.REF_SIZE;
          if(object instanceof Byte) return SizeOf.BYTE_SIZE+SizeOf.REF_SIZE;
        }
       
       
        if(object instanceof Object[]) {
           Object[] arr=(Object[]) object;
           long size=SizeOf.REF_SIZE;
           for(int i=0;i<arr.length;i++){
             size+=_size(arr[i], instances, dictionary, maxDepth - 1, maxSize);
           }
           return size;
        }
       
        if(object instanceof Map) {
          long size=SizeOf.REF_SIZE;
          Map.Entry entry;
          Map map=(Map) object;
          Iterator it = map.entrySet().iterator();
          while(it.hasNext()){
            entry=(Entry) it.next();
            size+=SizeOf.REF_SIZE;
            size+=_size(entry.getKey(), instances, dictionary, maxDepth - 1, maxSize);
            size+=_size(entry.getValue(), instances, dictionary, maxDepth - 1, maxSize);
          }
          return size;
      }
        if(object instanceof List) {
          long size=SizeOf.REF_SIZE;
          List list=(List) object;
          Iterator it = list.iterator();
          while(it.hasNext()){
            size+=_size(it.next(), instances, dictionary, maxDepth - 1, maxSize);
          }
          return size;
      }
         
       
        Class clazz = object.getClass();
       
        Meta cmd = Meta.getMetaData(clazz, dictionary);
        long shallowSize = cmd.calcInstanceSize(object);
        long size = shallowSize;
        if (clazz.isArray() && !cmd.getComponentClass().isPrimitive()) {
          for (int i=Array.getLength(object)-1; i>=0;i--) {
                size += _size(Array.get(object, i), instances, dictionary, maxDepth - 1, maxSize - size);
            }
        }
        else {
            List values = cmd.getReferenceFieldValues(object);
            Iterator it = values.iterator();
            while(it.hasNext()) {
              size += _size(it.next(), instances, dictionary, maxDepth - 1, maxSize - size);
            }
        }
        return size;
    }

  public static long size(long value) {
    return SizeOf.LONG_SIZE;
  }


  public static long size(boolean value) {
    return SizeOf.BOOLEAN_SIZE;
  }
}


class Meta {
   

    /** Class for which this metadata applies */
    private Class aClass;

    /** Parent class's metadata */
    private Meta superMetaData;

    private boolean isArray;

    /** Only filled if this is an array */
    private int componentSize;

    /** Only filled if this class is an array */
    private Class componentClass;

    /** Number of bytes reserved for the fields of this class and its parents,
     *  aligned to a word boundary. */
    private int fieldSize;

    /** this.fieldSize + super.totalFieldSize */
    private int totalFieldSize;

    /** Number of bytes reserved for instances of this class, aligned to a
     * 8 bytes boundary */
    private int instanceSize;

    private List referenceFields = new ArrayList();

    public static Meta getMetaData(Class aClass) {
        return getMetaData(aClass, new HashMap());
    }

    public static Meta getMetaData(Class aClass, Map dictionary) {
        if (aClass == null) {
            throw new IllegalArgumentException("aClass argument cannot be null");
        }

        if (aClass.isPrimitive()) {
            throw new IllegalArgumentException("ClassMetaData not supported for primitive types: " + aClass.getName());
        }

        if (dictionary.containsKey(aClass)) {
            return (Meta) dictionary.get(aClass);
        }
        Meta result = new Meta(aClass, dictionary);
        dictionary.put(aClass, result);
        return result;
    }

    private Meta(Class aClass, Map dictionary) {
       
        Class parentClass = aClass.getSuperclass();
        if (parentClass != null) {
            superMetaData = getMetaData(parentClass, dictionary);
        }

        this.aClass = aClass;

        if (aClass.isArray()) {
            processArrayClass(aClass);
        } else {
            processRegularClass(aClass);
            processFields(aClass);
        }
    }

    private static int getComponentSize(Class componentClass) {
        if (componentClass.equals(Double.TYPE) ||
            componentClass.equals(Long.TYPE)) {
            return SizeOf.LONG_SIZE;
        }

        if (componentClass.equals(Integer.TYPE) ||
            componentClass.equals(Float.TYPE)) {
            return SizeOf.INT_SIZE;
        }

        if (componentClass.equals(Character.TYPE) ||
            componentClass.equals(Short.TYPE)) {
            return SizeOf.SHORT_SIZE;
        }

        if (componentClass.equals(Byte.TYPE) ||
            componentClass.equals(Boolean.TYPE))  {
            return SizeOf.BYTE_SIZE;
        }

        return SizeOf.REF_SIZE;
    }

    public int getFieldSize() {
        return fieldSize;
    }

    private void processArrayClass(Class aClass) {
        isArray = true;
        componentClass = aClass.getComponentType();
        componentSize = getComponentSize(componentClass);
        instanceSize = SizeOf.HEADER_SIZE + SizeOf.WORD_SIZE;
    }

    private void processFields(Class aClass) {
        Field[] fields = aClass.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            int fieldModifier = field.getModifiers();
            if (Modifier.isStatic(fieldModifier)) {
                continue;
            }
            Class fieldType = field.getType();
            if (fieldType.isPrimitive()) {
                continue;
            }
            field.setAccessible(true);
            referenceFields.add(field);
        }
    }

    private void processRegularClass(Class aClass) {
        Field[] fields = aClass.getDeclaredFields();

        int longCount  = 0;
        int intCount   = 0;
        int shortCount = 0;
        int byteCount  = 0;
        int refCount   = 0;

        // Calculate how many fields of each type we have.
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            int fieldModifiers = field.getModifiers();
            if (Modifier.isStatic(fieldModifiers)) {
                continue;
            }

            Class fieldClass = field.getType();

            if (fieldClass.equals(Double.TYPE) ||
                fieldClass.equals(Long.TYPE)) {
                longCount++;
            } else if (fieldClass.equals(Integer.TYPE) ||
                fieldClass.equals(Float.TYPE)) {
                intCount++;
            } else if (fieldClass.equals(Character.TYPE) ||
                fieldClass.equals(Short.TYPE)) {
                shortCount++;
            }else if (fieldClass.equals(Byte.TYPE) ||
                fieldClass.equals(Boolean.TYPE))  {
                byteCount++;
            } else {
                refCount++;
            }
        }

        int localIntCount = intCount;
        int localShortCount = shortCount;
        int localByteCount = byteCount;
        int localRefCount = refCount;
       
        int parentTotalFieldSize = superMetaData == null ? 0 : superMetaData.getFieldSize();

        int alignedParentSize = align(parentTotalFieldSize, SizeOf.OBJECT_GRANULARITY_IN_BYTES);
        int paddingSpace = alignedParentSize - parentTotalFieldSize;
        // we try to pad with ints, and then shorts, and then bytes, and then refs.
        while (localIntCount > 0 && paddingSpace >= SizeOf.INT_SIZE) {
            paddingSpace -= SizeOf.INT_SIZE;
            localIntCount--;
        }

        while(localShortCount > 0 && paddingSpace >= SizeOf.SHORT_SIZE) {
            paddingSpace -= SizeOf.SHORT_SIZE;
            localShortCount--;
        }

        while (localByteCount > 0 && paddingSpace >= SizeOf.BYTE_SIZE) {
            paddingSpace -= SizeOf.BYTE_SIZE;
            localByteCount--;
        }

        while (localRefCount > 0 && paddingSpace >= SizeOf.REF_SIZE) {
            paddingSpace -= SizeOf.REF_SIZE;
            localRefCount--;
        }

        int preFieldSize = paddingSpace +
                longCount * SizeOf.LONG_SIZE +
                intCount * SizeOf.INT_SIZE +
                shortCount * SizeOf.SHORT_SIZE +
                byteCount * SizeOf.BYTE_SIZE +
                refCount * SizeOf.REF_SIZE;

        fieldSize = align(preFieldSize, SizeOf.REF_SIZE);
       
        totalFieldSize = parentTotalFieldSize + fieldSize;

        instanceSize = align(SizeOf.HEADER_SIZE + totalFieldSize, SizeOf.OBJECT_GRANULARITY_IN_BYTES);
    }

    public int calcArraySize(int length) {
        return align(instanceSize + componentSize * length, SizeOf.OBJECT_GRANULARITY_IN_BYTES);
    }

    public int calcInstanceSize(Object instance) {
        if (instance == null) {
            throw new IllegalArgumentException("Parameter cannot be null");
        }
        if (!instance.getClass().equals(aClass)) {
            throw new IllegalArgumentException("Parameter not of proper class.  Was: " + instance.getClass() + " expected: " + aClass);
        }

        if (isArray) {
            int length = Array.getLength(instance);
            return calcArraySize(length);
        }
        return instanceSize;
    }

    public List getReferenceFieldValues(Object instance) {
        if (instance == null) {
            throw new IllegalArgumentException("Parameter cannot be null.");
        }

        List results;
        if (superMetaData == null) {
            results = new ArrayList();
        } else {
            results = superMetaData.getReferenceFieldValues(instance);
        }
        Iterator it = referenceFields.iterator();
        Field field;
        while(it.hasNext()) {
          field=(Field) it.next();
            Object value;
            try {
                value = field.get(instance);
            } catch (IllegalAccessException ex) {
                // Should never happen in practice.
                throw new IllegalStateException("Unexpected exeption: "+ ex.getMessage());
            }
            if (value != null) {
                results.add(value);
            }
        }
        return results;
    }

    public Meta getSuperMetaData() {
        return superMetaData;
    }

    public int getInstanceSize() {
        return instanceSize;
    }

    public int getTotalFieldSize() {
        return totalFieldSize;
    }

    public Class getTheClass() {
        return aClass;
    }

    public Class getComponentClass() {
        return componentClass;
    }
   
    public static int align(int size, int granularity) {
        return size + (granularity - size % granularity) % granularity;
    }
}

   
    class Architecture {

      private static final Architecture ARCH_32_BITS=new Architecture(32, 4);
      private static final Architecture ARCH_64_BITS=new Architecture(64, 8);
      private static final Architecture ARCH_UNKNOWN=new Architecture(32, 4);
       
        private int bits;
        private int wordSize;

        private Architecture(int bits, int wordSize) {
            this.bits = bits;
            this.wordSize = wordSize;
        }

        public int getBits() {
            return bits;
        }

        public int getWordSize() {
            return wordSize;
        }

        public static Architecture getVMArchitecture() {
            if (SystemUtil.getJREArch()==SystemUtil.ARCH_32)
              return ARCH_32_BITS;
            else if (SystemUtil.getJREArch()==SystemUtil.ARCH_64)
              return ARCH_64_BITS;
           
            return ARCH_UNKNOWN;
        }
    }
TOP

Related Classes of railo.commons.lang.SizeOf

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.