KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > util > SizeOf


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.util;
11
12 // necessary for SizeOf
13
import java.lang.reflect.Array JavaDoc;
14 import java.lang.reflect.Field JavaDoc;
15 import java.lang.reflect.Modifier JavaDoc;
16 import java.util.*;
17
18 import org.mmbase.util.logging.Logger;
19 import org.mmbase.util.logging.Logging;
20
21 /**
22  *<p>
23  * Implementation of 'sizeof'. It is very hard to get reasonable estimations of how much memory your
24  * structures take, this class it trying it any way. Often it's quite a heavy operation, so don't
25  * use it too much.
26  *</p>
27  * <p>
28  * A count of the byte size of an object is done recursively and for every count a SizeOf instance
29  * must be instantiated. The static {@link #getByteSize(Object)} does that for you.
30  *</p>
31  * <p>
32  * The core of the SizeOf object is then {@link #sizeof(Object)} plus a (private) 'countedObjects'
33  * collection. The sizeof method returns the size the given Object would increase the size of this
34  * countedObjects administration. This means that it returns 0 if the Object was already measured by the SizeOf
35  * instance.
36  * </p>
37  * <p>
38  * This means that it can be tricky to interpret the results of sizeof. The basic rule is that you
39  * should take the size of a lot of similar objects with the same SizeOf instance, and take the
40  * average.
41  * </p>
42  * <p>
43  * A good example is {@link org.mmbase.module.core.MMObjectNode}. The first one counted will also
44  * count a {@link org.mmbase.module.core.MMObjectBuilder} object - because that is linked to it, so
45  * its memory is also taken (indirectly) - and will therefor give an unexpectedly large value like 20
46  * kb or so. The second MMObjectNode, of the same type, that you'd count would however give a much
47  * better estimation of the memory used by one Node in MMBase. The MMObjectBuilder is not counted
48  * any more in this second Object, because it was already counted because of the first one.
49  * </p>
50  * <p>
51  * For every individual entity there are several strategies for guessing the size.
52  * <ul>
53  * <li>For atomic types (boolean, byte, char, short, int, long, float, double) a constant value is
54  * returned</li>
55  * <li>If the entity implements {@link SizeMeasurable} it uses {@link SizeMeasurable#getByteSize(SizeOf)}</li>
56  * <li>For a limited number of known classes, a reasonable guess is done. E.g. if it is a {@link
57  * java.util.Collection} it will simply sum the results of sizeof of its elements, and for a String
58  * it will return getBytes().length().</li>
59  * <li>If all that fails, reflection will be used to sum the results of sizeof of all readable
60  * members.</li>
61  * </ul>
62  * </p>
63  * <p>
64  * Don't forget to dereference or clear the SizeOf after use, otherwise it itself is a memory leak.
65  *</p>
66  *
67  * @author Michiel Meeuwissen
68  * @since MMBase-1.6
69  * @version $Id: SizeOf.java,v 1.17 2006/06/19 05:57:57 michiel Exp $
70  * @todo We need to know how well this actually works...
71  */

72 public class SizeOf {
73     private static final Logger log = Logging.getLoggerInstance(SizeOf.class);
74
75     public static final int SZ_REF = 4;
76     private static int size_prim(Class JavaDoc t) {
77         if (t == Boolean.TYPE) return 1;
78         else if (t == Byte.TYPE) return 1;
79         else if (t == Character.TYPE) return 2;
80         else if (t == Short.TYPE) return 2;
81         else if (t == Integer.TYPE) return 4;
82         else if (t == Long.TYPE) return 8;
83         else if (t == Float.TYPE) return 4;
84         else if (t == Double.TYPE) return 8;
85         else if (t == Void.TYPE) return 0;
86         else return SZ_REF;
87     }
88
89     public static int sizeof(boolean b) { return 1; }
90     public static int sizeof(byte b) { return 1; }
91     public static int sizeof(char c) { return 2; }
92     public static int sizeof(short s) { return 2; }
93     public static int sizeof(int i) { return 4; }
94     public static int sizeof(long l) { return 8; }
95     public static int sizeof(float f) { return 4; }
96     public static int sizeof(double d) { return 8; }
97
98     // To avoid infinite loops (cyclic references):
99
private Set countedObjects = new HashSet();
100
101     public static int getByteSize(Object JavaDoc obj) {
102         return new SizeOf().sizeof(obj);
103     }
104
105     /**
106      * Makes this SizeOf object ready for reuse.
107      * @since MMBase-1.8
108      */

109     public void clear() {
110         countedObjects.clear();
111     }
112     /**
113      * @return The size in bytes obj structure will take, or <code>0</code> if the object was
114      * already counted by this SizeOf object.
115      */

116     public int sizeof(Object JavaDoc obj) {
117         if (obj == null) {
118             return 0;
119         }
120
121         if (!countedObjects.add(obj)) {
122             return 0;
123         }
124
125         Class JavaDoc c = obj.getClass();
126
127         if (c.isArray()) {
128             log.debug("an array");
129             return size_arr(obj, c);
130         } else {
131             if (log.isDebugEnabled()) {
132                 log.debug("an object " + obj + " " + c);
133             }
134             try {
135                 if (SizeMeasurable.class.isAssignableFrom(c)) return sizeof((SizeMeasurable) obj);
136                 if (javax.servlet.http.HttpSession JavaDoc.class.isAssignableFrom(c)) return sizeof((javax.servlet.http.HttpSession JavaDoc) obj);
137                 if (org.w3c.dom.Node JavaDoc.class.isAssignableFrom(c)) return sizeof((org.w3c.dom.Node JavaDoc) obj);
138                 if (Map.class.isAssignableFrom(c)) return sizeof((Map) obj);
139                 if (Collection.class.isAssignableFrom(c)) return sizeof((Collection) obj);
140                 if (String JavaDoc.class.isAssignableFrom(c)) return sizeof((String JavaDoc) obj);
141                 // it's odd that all these seem to cost 16 bytes each, but this is a result of simply trying it on Sun JVM 1.5
142
// See also http://www.javaworld.com/javaworld/javatips/jw-javatip130.html
143
if (Integer JavaDoc.class.isAssignableFrom(c)) return 16;
144                 if (Long JavaDoc.class.isAssignableFrom(c)) return 16;
145                 if (Float JavaDoc.class.isAssignableFrom(c)) return 16;
146                 if (Double JavaDoc.class.isAssignableFrom(c)) return 16;
147                 // more insteresting stuff can be added here.
148
} catch (Throwable JavaDoc e) {
149                 log.warn("Error during determination of size of " + obj + " :" + e.getMessage(), e);
150             }
151             return size_inst(obj, c);
152         }
153     }
154
155     private int sizeof(Map m) {
156         int len =
157             size_inst(m, m.getClass()) +
158             m.size() * 30; // estimated overhead per entry. Is about correct for Hashtable and HashMap
159
Iterator i = m.entrySet().iterator();
160         while (i.hasNext()) {
161             Map.Entry entry = (Map.Entry) i.next();
162             len += sizeof(entry.getKey());
163             len += sizeof(entry.getValue());
164         }
165         return len;
166     }
167
168     private int sizeof(Collection m) {
169         log.debug("sizeof List" );
170         int len = size_inst(m, m.getClass());
171         Iterator i = m.iterator();
172         while (i.hasNext()) {
173             len += sizeof(i.next());
174         }
175         return len;
176     }
177
178     private int sizeof(javax.servlet.http.HttpSession JavaDoc session) {
179         log.debug("sizeof HttpSession");
180         int len = size_inst(session, session.getClass());
181         Enumeration e = session.getAttributeNames();
182         while (e.hasMoreElements()) {
183             String JavaDoc attribute = (String JavaDoc) e.nextElement();
184             len += sizeof(attribute);
185             len += sizeof(session.getAttribute(attribute));
186         }
187         return len;
188     }
189
190     private int sizeof(org.w3c.dom.Node JavaDoc node) {
191         log.debug("sizeof Node");
192         // a little hackish...
193
return sizeof(org.mmbase.util.xml.XMLWriter.write(node, false));
194     }
195
196     private int sizeof(String JavaDoc m) {
197         // just a guess, but on a Sun 1.5 JVM this is pretty good.
198
return 44 + m.length() * 2; // probably 11 (!) references + array of 16 bits unicode?
199
}
200
201
202     private int sizeof(SizeMeasurable m) {
203         if (log.isDebugEnabled()) {
204             log.debug("sizeof SizeMeasureable " + m);
205         }
206         return m.getByteSize(this);
207     }
208
209
210     private int size_inst(Object JavaDoc obj, Class JavaDoc c) {
211         Field JavaDoc flds[] = c.getDeclaredFields();
212         int sz = 8; // wild guess for the size of an Object. (reference + hashcode?
213

214         for (int i = 0; i < flds.length; i++) {
215             Field JavaDoc f = flds[i];
216             if (!c.isInterface() && (f.getModifiers() & Modifier.STATIC) != 0) {
217                 continue;
218             }
219             sz += size_prim(f.getType());
220             //f.setAccessible(true);
221
if (f.isAccessible()) {
222                 try {
223                     sz += sizeof(f.get(obj)); // recursion
224
} catch (java.lang.IllegalAccessException JavaDoc e) {
225                     // well...
226
log.trace(e);
227                 }
228             }
229         }
230
231         if (c.getSuperclass() != null) {
232             sz += size_inst(obj, c.getSuperclass()) - 8; // 8: already guessed
233
}
234
235         Class JavaDoc cv[] = c.getInterfaces();
236         for (int i = 0; i < cv.length; i++) {
237             sz += size_inst(obj, cv[i]) - 8; // 8: already guessed
238
}
239
240         return sz;
241     }
242
243     private int size_arr(Object JavaDoc obj, Class JavaDoc c) {
244         Class JavaDoc ct = c.getComponentType();
245         int len = Array.getLength(obj);
246
247         if (ct.isPrimitive()) {
248             return len * size_prim(ct);
249         } else {
250             int sz = 0;
251             for (int i = 0; i < len; i++) {
252                 Object JavaDoc obj2 = Array.get(obj, i);
253                 sz += sizeof(obj2);
254             }
255             return sz;
256         }
257     }
258
259     public static void main(String JavaDoc argv[]) throws InterruptedException JavaDoc {
260         final Runtime JavaDoc rt = Runtime.getRuntime();
261         final int SIZE = Math.round((float) Math.pow(10, 4)); // I hate java
262

263         //final Object[] list = new Object[SIZE];
264
List list = new ArrayList();
265         //ArrayList list = new ArrayList();
266
//Map map = new HashMap();
267

268         rt.runFinalization();
269         rt.gc();
270         Thread.sleep(1000);
271         long usedBefore = rt.totalMemory() - rt.freeMemory();
272         // create one million objects
273
for (int i = SIZE; i < 2 * SIZE; i++) {
274             //list[i - 1000000] = "a" + i + "b" + i;
275
//list.add("a" + i + "b" + i); // of 16 byte
276
//list.add(new String( new byte[] {})); // of 0 byte
277
//list.add(new String( new byte[] {}).intern());
278
//map.put("a" + i , "b" + i); // of 16 byte
279
list.add(new Integer JavaDoc(i));
280             //list[i - SIZE] = new Double(i);
281
}
282         rt.runFinalization();
283         rt.gc();
284         Thread.sleep(1000);
285         long usedAfter = rt.totalMemory() - rt.freeMemory();
286         System.out.println("" + SIZE +" of Integer costs " + (usedAfter - usedBefore) + " bytes");
287         System.out.println("Sizeof reports: " + SizeOf.getByteSize(list) + " bytes");
288         //System.out.println("" + list);
289

290
291     }
292 }
293
294
Popular Tags