001 package railo.commons.lang; 002 003 import java.io.ByteArrayOutputStream; 004 import java.io.ObjectOutputStream; 005 import java.lang.reflect.Method; 006 import java.util.HashSet; 007 import java.util.Iterator; 008 import java.util.List; 009 import java.util.Map; 010 import java.util.Set; 011 012 import railo.loader.engine.CFMLEngineFactory; 013 import railo.loader.util.Util; 014 import railo.runtime.exp.PageException; 015 import railo.runtime.type.Collection; 016 import railo.runtime.type.Query; 017 import railo.runtime.util.Creation; 018 019 public class SizeAndCount { 020 021 private static final int OBJECT_GRANULARITY_IN_BYTES = 8; 022 private static final int WORD_SIZE = Arch.getVMArchitecture().getWordSize(); 023 private static final int HEADER_SIZE = 2 * WORD_SIZE; 024 025 private static final int DOUBLE_SIZE = 8; 026 private static final int FLOAT_SIZE = 4; 027 private static final int LONG_SIZE = 8; 028 private static final int INT_SIZE = 4; 029 private static final int SHORT_SIZE = 2; 030 private static final int BYTE_SIZE = 1; 031 private static final int BOOLEAN_SIZE = 1; 032 private static final int CHAR_SIZE = 2; 033 private static final int REF_SIZE = WORD_SIZE; 034 035 public static Size sizeOf(Object obj) throws PageException { 036 Creation creator = CFMLEngineFactory.getInstance().getCreationUtil(); 037 Size size = new Size(0,0); 038 sizeOf(creator,size, obj, new HashSet<Object>()); 039 return size; 040 } 041 042 private static void sizeOf(Creation creator,Size size,Object obj, Set<Object> parents) throws PageException { 043 if(obj==null) return; 044 Object raw=obj; 045 046 // TODO this is just a patch solution, find a better way to handle this kind of situation (Wrapper classes) 047 if(isInstaneOf(obj.getClass(),"railo.runtime.text.xml.struct.XMLStruct")) { 048 try { 049 Method toNode = raw.getClass().getMethod("toNode", new Class[0]); 050 raw=toNode.invoke(obj, new Object[0]); 051 } 052 catch (Throwable e) { 053 e.printStackTrace(); 054 } 055 } 056 057 058 if(parents.contains(raw)) return; 059 parents.add(raw); 060 try { 061 if(obj instanceof Collection) { 062 if(obj instanceof Query) 063 sizeOf(creator,size,(Query)obj,parents); 064 else 065 sizeOf(creator,size,((Collection)obj).valueIterator(),parents); 066 return; 067 } 068 // Map 069 else if(obj instanceof Map) { 070 sizeOf(creator,size,((Map)obj).values().iterator(),parents); 071 return; 072 } 073 // List 074 else if(obj instanceof List) { 075 sizeOf(creator,size,((List)obj).iterator(),parents); 076 return; 077 } 078 079 // String 080 else if(obj instanceof String){ 081 size.size+= (CHAR_SIZE*((String)obj).length())+REF_SIZE; 082 } 083 // Number 084 else if(obj instanceof Number){ 085 if(obj instanceof Double) size.size+= DOUBLE_SIZE+REF_SIZE; 086 else if(obj instanceof Float) size.size+= FLOAT_SIZE+REF_SIZE; 087 else if(obj instanceof Long) size.size+= LONG_SIZE+REF_SIZE; 088 else if(obj instanceof Integer) size.size+= INT_SIZE+REF_SIZE; 089 else if(obj instanceof Short) size.size+= SHORT_SIZE+REF_SIZE; 090 else if(obj instanceof Byte) size.size+= BYTE_SIZE+REF_SIZE; 091 } 092 093 else if(obj instanceof Boolean) size.size+= REF_SIZE+BOOLEAN_SIZE; 094 else if(obj instanceof Character) size.size+= REF_SIZE+CHAR_SIZE; 095 096 else size.size+=_sizeOf(obj); 097 098 size.count++; 099 } 100 finally { 101 //parents.remove(raw);// TODO should we not remove, to see if sister is me. 102 } 103 } 104 105 private static void sizeOf(Creation creator,Size size,Iterator it, Set<Object> parents) throws PageException { 106 size.count++; 107 size.size+=REF_SIZE; 108 while(it.hasNext()){ 109 sizeOf(creator,size,it.next(),parents); 110 } 111 } 112 113 private static void sizeOf(Creation creator,Size size,Query qry, Set<Object> parents) throws PageException { 114 size.count++; 115 size.size+=REF_SIZE; 116 int rows=qry.getRecordcount(); 117 String[] strColumns = qry.getColumns(); 118 Collection.Key[] columns = new Collection.Key[strColumns.length]; 119 for(int col=0;col<columns.length;col++){ 120 columns[col]=creator.createKey(strColumns[col]); 121 } 122 123 124 for(int row=1;row<=rows;row++){ 125 for(int col=0;col<columns.length;col++){ 126 sizeOf(creator,size,qry.getAt(columns[col], row),parents); 127 } 128 } 129 } 130 131 public static boolean isInstaneOf(Class src ,String className) { 132 if(src==null) return false; 133 if(className.equals(src.getName()))return true; 134 135 // interfaces 136 Class[] interfaces = src.getInterfaces(); 137 for(int i=0;i<interfaces.length;i++){ 138 if(isInstaneOf(interfaces[i], className)) return true; 139 } 140 return isInstaneOf(src.getSuperclass(), className); 141 } 142 143 public static int _sizeOf(Object o) { 144 //System.err.println(o.getClass().getName()); 145 ByteArrayOutputStream os = new ByteArrayOutputStream(); 146 ObjectOutputStream oos=null; 147 try { 148 oos = new ObjectOutputStream(os); 149 oos.writeObject(o); 150 } 151 catch(Throwable t){} 152 finally { 153 Util.closeEL(oos); 154 } 155 return os.toByteArray().length; 156 } 157 158 159 public static class Size { 160 161 public int count; 162 public int size; 163 164 public Size(int count, int size) { 165 this.count=count; 166 this.size=size; 167 } 168 169 } 170 171 } 172 173 174 class Arch { 175 176 private static final Arch ARCH_32_BITS=new Arch(32, 4); 177 private static final Arch ARCH_64_BITS=new Arch(64, 8); 178 private static final Arch ARCH_UNKNOWN=new Arch(32, 4); 179 180 private int bits; 181 private int wordSize; 182 183 private Arch(int bits, int wordSize) { 184 this.bits = bits; 185 this.wordSize = wordSize; 186 } 187 188 public int getBits() { 189 return bits; 190 } 191 192 public int getWordSize() { 193 return wordSize; 194 } 195 196 public static Arch getVMArchitecture() { 197 String archString = System.getProperty("sun.arch.data.model"); 198 if (archString != null) { 199 if (archString.equals("32")) { 200 return ARCH_32_BITS; 201 } else if (archString.equals("64")) { 202 return ARCH_64_BITS; 203 } 204 } 205 return ARCH_UNKNOWN; 206 } 207 208 }