001: /*
002: * TclObjectMemory.java
003: *
004: * Copyright (c) 1997 Cornell University.
005: * Copyright (c) 1997 Sun Microsystems, Inc.
006: *
007: * See the file "license.terms" for information on usage and
008: * redistribution of this file, and for a DISCLAIMER OF ALL
009: * WARRANTIES.
010: *
011: * RCS: @(#) $Id: TclObjectMemory.java,v 1.1 2006/06/07 01:53:51 mdejong Exp $
012: *
013: */
014:
015: package tcl.lang;
016:
017: import java.util.*;
018: import java.lang.reflect.*;
019: import java.util.*;
020:
021: /**
022: * This class is used to test memory space requrements for TclObject
023: * and related classes.
024: */
025:
026: class TclObjectMemory implements Command {
027:
028: public void cmdProc(Interp interp, TclObject[] objv)
029: throws TclException {
030: if (objv.length != 1) {
031: throw new TclNumArgsException(interp, 1, objv, "");
032: }
033:
034: // Report memory sizes for common TclObjects
035:
036: StringBuffer sb = new StringBuffer();
037: MemoryCounter mc = new MemoryCounter();
038: long bytes;
039: TclObject tobj;
040:
041: tobj = TclString.newInstance("hi");
042: bytes = mc.estimate(tobj);
043: sb.append("TclObject " + bytes + " bytes");
044: sb.append("\n");
045:
046: bytes = mc.estimate(tobj.getInternalRep());
047: sb.append("TclString " + bytes + " bytes");
048: sb.append("\n");
049:
050: tobj = TclInteger.newInstance(1);
051: bytes = mc.estimate(tobj.getInternalRep());
052: sb.append("TclInteger " + bytes + " bytes");
053: sb.append("\n");
054:
055: tobj = TclDouble.newInstance(1.0);
056: bytes = mc.estimate(tobj.getInternalRep());
057: sb.append("TclDouble " + bytes + " bytes");
058: sb.append("\n");
059:
060: tobj = TclList.newInstance();
061: bytes = mc.estimate(tobj.getInternalRep());
062: sb.append("TclList " + bytes + " bytes");
063:
064: interp.setResult(sb.toString());
065: }
066: }
067:
068: class MemorySizes {
069: private final Map primitiveSizes = new IdentityHashMap() {
070: {
071: put(boolean.class, new Integer(1));
072: put(byte.class, new Integer(1));
073: put(char.class, new Integer(2));
074: put(short.class, new Integer(2));
075: put(int.class, new Integer(4));
076: put(float.class, new Integer(4));
077: put(double.class, new Integer(8));
078: put(long.class, new Integer(8));
079: }
080: };
081:
082: public int getPrimitiveFieldSize(Class clazz) {
083: return ((Integer) primitiveSizes.get(clazz)).intValue();
084: }
085:
086: public int getPrimitiveArrayElementSize(Class clazz) {
087: return getPrimitiveFieldSize(clazz);
088: }
089:
090: public int getPointerSize() {
091: return 4;
092: }
093:
094: public int getClassSize() {
095: return 8;
096: }
097: }
098:
099: /**
100: * This class can estimate how much memory an Object uses. It is
101: * fairly accurate for JDK 1.4.2. It is based on the newsletter #29.
102: */
103: class MemoryCounter {
104: private static final MemorySizes sizes = new MemorySizes();
105: private final Map visited = new IdentityHashMap();
106: private final Stack stack = new Stack();
107:
108: public synchronized long estimate(Object obj) {
109: long result = _estimate(obj);
110: while (!stack.isEmpty()) {
111: result += _estimate(stack.pop());
112: }
113: visited.clear();
114: return result;
115: }
116:
117: private boolean skipObject(Object obj) {
118: if (obj instanceof String) {
119: // this will not cause a memory leak since
120: // unused interned Strings will be thrown away
121: if (obj == ((String) obj).intern()) {
122: return true;
123: }
124: }
125: return (obj == null) || visited.containsKey(obj);
126: }
127:
128: private long _estimate(Object obj) {
129: if (skipObject(obj))
130: return 0;
131: visited.put(obj, null);
132: long result = 0;
133: Class clazz = obj.getClass();
134: if (clazz.isArray()) {
135: return _estimateArray(obj);
136: }
137: while (clazz != null) {
138: Field[] fields = clazz.getDeclaredFields();
139: for (int i = 0; i < fields.length; i++) {
140: if (!Modifier.isStatic(fields[i].getModifiers())) {
141: if (fields[i].getType().isPrimitive()) {
142: result += sizes.getPrimitiveFieldSize(fields[i]
143: .getType());
144: } else {
145: result += sizes.getPointerSize();
146: fields[i].setAccessible(true);
147: try {
148: Object toBeDone = fields[i].get(obj);
149: if (toBeDone != null) {
150: stack.add(toBeDone);
151: }
152: } catch (IllegalAccessException ex) {
153: }
154: }
155: }
156: }
157: clazz = clazz.getSuperclass();
158: }
159: result += sizes.getClassSize();
160: return roundUpToNearestEightBytes(result);
161: }
162:
163: private long roundUpToNearestEightBytes(long result) {
164: if ((result % 8) != 0) {
165: result += 8 - (result % 8);
166: }
167: return result;
168: }
169:
170: protected long _estimateArray(Object obj) {
171: long result = 16;
172: int length = Array.getLength(obj);
173: if (length != 0) {
174: Class arrayElementClazz = obj.getClass().getComponentType();
175: if (arrayElementClazz.isPrimitive()) {
176: result += length
177: * sizes
178: .getPrimitiveArrayElementSize(arrayElementClazz);
179: } else {
180: for (int i = 0; i < length; i++) {
181: result += sizes.getPointerSize()
182: + _estimate(Array.get(obj, i));
183: }
184: }
185: }
186: return result;
187: }
188:
189: public static void main(String[] args) throws Exception {
190: String name1 = args[0];
191: String name2 = args[1];
192:
193: MemoryCounter mc = new MemoryCounter();
194:
195: Class cl = Class.forName(name1);
196: long size = mc.estimate(cl.newInstance());
197: System.out.println("sizeof " + name1 + " is " + size);
198:
199: cl = Class.forName(name2);
200: size = mc.estimate(cl.newInstance());
201: System.out.println("sizeof " + name2 + " is " + size);
202: }
203: }
|