001: /*
002:
003: Derby - Class org.apache.derby.iapi.services.cache.ClassSize
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.services.cache;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025:
026: import java.lang.Class;
027: import java.lang.reflect.Field;
028: import java.lang.Runtime;
029: import java.lang.InterruptedException;
030: import java.lang.reflect.Modifier;
031:
032: public class ClassSize {
033: public static final int refSize;
034: private static final int objectOverhead = 2; // references, not bytes!
035: private static final int booleanSize = 4;
036: private static final int charSize = 4; // Unicode
037: private static final int shortSize = 4;
038: private static final int intSize = 4;
039: private static final int longSize = 8;
040: private static final int floatSize = 4;
041: private static final int doubleSize = 8;
042: private static final int minObjectSize;
043:
044: private static boolean dummyCatalog = false; // Used when constructing the catalog to prevent recursion
045:
046: static boolean noGuess = false;
047: // noGuess is used in unit testing.
048:
049: static boolean unitTest = false;
050: // unitTest is used in unit testing
051:
052: private static final int[] wildGuess = { 0, 16 };
053: /* The standard wild guess of the size of an unknown class, the size of 16 references.
054: * Used when the security manager will not let us look at the class fields.
055: */
056:
057: /* Do not let the compiler see ClassSizeCatalog. Otherwise it will try to
058: * compile it. This may fail because ClassSizeCatalog.java is not created
059: * until everything else has been compiled. Bury ClassSizeCatalog in a string.
060: */
061: private static java.util.Hashtable catalog;
062: static {
063: try {
064: catalog = (java.util.Hashtable) Class
065: .forName(
066: "org.apache.derby.iapi.services.cache.ClassSizeCatalog")
067: .newInstance();
068: } catch (Exception e) {
069: }
070: ;
071:
072: // Figure out whether this is a 32 or 64 bit machine.
073: Runtime runtime = Runtime.getRuntime();
074: long memBase = runtime.totalMemory() - runtime.freeMemory();
075: Object[] junk = new Object[10000];
076: long memUsed = runtime.totalMemory() - runtime.freeMemory()
077: - memBase;
078: int sz = (int) ((memUsed + junk.length / 2) / junk.length);
079: refSize = (4 > sz) ? 4 : sz;
080: minObjectSize = 4 * refSize;
081: }
082:
083: /**
084: * do not try to use the catalog.
085: */
086: public static void setDummyCatalog() {
087: dummyCatalog = true;
088: }
089:
090: /**
091: * Get the estimate of the size of an object reference.
092: *
093: * @return the estimate in bytes.
094: */
095: public static int getRefSize() {
096: return refSize;
097: }
098:
099: /**
100: * @return the estimate of the size of a primitive int
101: */
102: public static int getIntSize() {
103: return intSize;
104: }
105:
106: /**
107: * The estimate of the size of a class instance depends on whether the JVM uses 32 or 64
108: * bit addresses, that is it depends on the size of an object reference. It is a linear
109: * function of the size of a reference, e.g.
110: * 24 + 5*r
111: * where r is the size of a reference (usually 4 or 8 bytes).
112: *
113: * This method returns the coefficients of the linear function, e.g. {24, 5} in the above
114: * example.
115: *
116: * @param cl A class whose instance size is to be estimated
117: * @return an array of 2 integers. The first integer is the constant part of the function,
118: * the second is the reference size coefficient.
119: */
120: public static int[] getSizeCoefficients(Class cl) {
121: int[] coeff = { 0, objectOverhead };
122:
123: for (; null != cl; cl = cl.getSuperclass()) {
124: Field[] field = cl.getDeclaredFields();
125: if (null != field) {
126: for (int i = 0; i < field.length; i++) {
127: if (!Modifier.isStatic(field[i].getModifiers())) {
128: Class fieldClass = field[i].getType();
129: if (fieldClass.isArray()
130: || !fieldClass.isPrimitive())
131: coeff[1]++;
132: else // Is simple primitive
133: {
134: String name = fieldClass.getName();
135:
136: if (name.equals("int") || name.equals("I"))
137: coeff[0] += intSize;
138: else if (name.equals("long")
139: || name.equals("J"))
140: coeff[0] += longSize;
141: else if (name.equals("boolean")
142: || name.equals("Z"))
143: coeff[0] += booleanSize;
144: else if (name.equals("short")
145: || name.equals("S"))
146: coeff[0] += shortSize;
147: else if (name.equals("byte")
148: || name.equals("B"))
149: coeff[0] += 1;
150: else if (name.equals("char")
151: || name.equals("C"))
152: coeff[0] += charSize;
153: else if (name.equals("float")
154: || name.equals("F"))
155: coeff[0] += floatSize;
156: else if (name.equals("double")
157: || name.equals("D"))
158: coeff[0] += doubleSize;
159: else
160: // What is this??
161: coeff[1]++; // Make a guess: one reference (?)
162: }
163: }
164: }
165: }
166: }
167: return coeff;
168: } // end of getSizeCoefficients
169:
170: /**
171: * Estimate the static space taken up by a class instance given the coefficients
172: * returned by getSizeCoefficients.
173: *
174: * @param coeff the coefficients
175: *
176: * @return the size estimate, in bytes
177: */
178: public static int estimateBaseFromCoefficients(int[] coeff) {
179: int size = coeff[0] + coeff[1] * refSize;
180: // Round up to a multiple of 8
181: size = (size + 7) / 8;
182: size *= 8;
183: return (size < minObjectSize) ? minObjectSize : size;
184: } // end of estimateBaseFromCoefficients
185:
186: /**
187: * Estimate the static space taken up by a class instance from cataloged coefficients.
188: *
189: * @param cls the class
190: *
191: * @return the size estimate, in bytes
192: *
193: * @see #estimateBaseFromCoefficients
194: * @see #getSizeCoefficients
195: * see org.apache.derbyBuild.ClassSizeCrawler
196: */
197: public static int estimateBaseFromCatalog(Class cls) {
198: return estimateBaseFromCatalog(cls, false);
199: }
200:
201: private static int estimateBaseFromCatalog(Class cls,
202: boolean addToCatalog) {
203: if (dummyCatalog)
204: return 0;
205:
206: if (SanityManager.DEBUG)
207: SanityManager.ASSERT(catalog != null,
208: "The class size catalog could not be initialized.");
209:
210: int[] coeff = (int[]) catalog.get(cls.getName());
211: if (coeff == null) {
212: try {
213: coeff = getSizeCoefficients(cls);
214: } catch (Throwable t) {
215: if (noGuess)
216: return -2;
217: coeff = wildGuess;
218: }
219: if (addToCatalog)
220: catalog.put(cls.getName(), coeff);
221: }
222: return estimateBaseFromCoefficients(coeff);
223: } // end of estimateBaseFromCatalog
224:
225: /**
226: * Estimate the static space taken up by a class instance. Save the coefficients
227: * in a catalog.
228: *
229: * @param cls the class
230: *
231: * @return the size estimate, in bytes
232: *
233: * @see #estimateBaseFromCoefficients
234: * @see #getSizeCoefficients
235: * see org.apache.derbyBuild.ClassSizeCrawler
236: */
237: public static int estimateAndCatalogBase(Class cls) {
238: return estimateBaseFromCatalog(cls, true);
239: } // end of estimateAndCatalogBase
240:
241: /**
242: * Estimate the static space taken up by the fields of a class. This includes the space taken
243: * up by by references (the pointer) but not by the referenced object. So the estimated
244: * size of an array field does not depend on the size of the array. Similarly the size of
245: * an object (reference) field does not depend on the object.
246: *
247: * @return the size estimate in bytes.
248: *
249: * Note that this method will throw a SecurityException if the SecurityManager does not
250: * let this class execute the method Class.getDeclaredFields(). If this is a concern try
251: * to compute the size coefficients at build time.
252: * see org.apache.derbyBuild.ClassSizeCrawler
253: * @see #estimateBaseFromCatalog
254: */
255: public static int estimateBase(Class cl) {
256: return estimateBaseFromCoefficients(getSizeCoefficients(cl));
257: } // End of estimateBase
258:
259: /**
260: * @return the estimated overhead of an array. The estimated size of an x[n] array is
261: * estimateArrayOverhead() + n*sizeOf(x).
262: */
263: public static int estimateArrayOverhead() {
264: return minObjectSize;
265: }
266:
267: /**
268: * Estimate the size of a Hashtable entry. In Java 1.2 we can use Map.entry, but this is not
269: * available in earlier versions of Java.
270: *
271: * @return the estimate, in bytes
272: */
273: public static int estimateHashEntrySize() {
274: return objectOverhead + 3 * refSize;
275: }
276:
277: /**
278: * Estimate the size of a string.
279: *
280: * @return the estimated size, in bytes
281: */
282: public static int estimateMemoryUsage(String str) {
283: if (null == str)
284: return 0;
285: // Since Java uses Unicode assume that each character takes 2 bytes
286: return 2 * str.length();
287: }
288: }
|