1 | /* |
2 | |
3 | Derby - Class org.apache.derby.iapi.services.cache.ClassSize |
4 | |
5 | Copyright 2002, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.iapi.services.cache; |
22 | |
23 | import org.apache.derby.iapi.services.sanity.SanityManager; |
24 | |
25 | import java.lang.Class; |
26 | import java.lang.reflect.Field; |
27 | import java.lang.Runtime; |
28 | import java.lang.InterruptedException; |
29 | import java.lang.reflect.Modifier; |
30 | |
31 | public class ClassSize |
32 | { |
33 | public static final int refSize; |
34 | private static final int objectOverhead = 2; // references, not bytes! |
35 | private static final int booleanSize = 4; |
36 | private static final int charSize = 4; // Unicode |
37 | private static final int shortSize = 4; |
38 | private static final int intSize = 4; |
39 | private static final int longSize = 8; |
40 | private static final int floatSize = 4; |
41 | private static final int doubleSize = 8; |
42 | private static final int minObjectSize; |
43 | |
44 | private static boolean dummyCatalog = false; // Used when constructing the catalog to prevent recursion |
45 | |
46 | static boolean noGuess = false; |
47 | // noGuess is used in unit testing. |
48 | |
49 | static boolean unitTest = false; |
50 | // unitTest is used in unit testing |
51 | |
52 | private static final int[] wildGuess = {0,16}; |
53 | /* The standard wild guess of the size of an unknown class, the size of 16 references. |
54 | * Used when the security manager will not let us look at the class fields. |
55 | */ |
56 | |
57 | /* Do not let the compiler see ClassSizeCatalog. Otherwise it will try to |
58 | * compile it. This may fail because ClassSizeCatalog.java is not created |
59 | * until everything else has been compiled. Bury ClassSizeCatalog in a string. |
60 | */ |
61 | private static java.util.Hashtable catalog; |
62 | static |
63 | { |
64 | try |
65 | { |
66 | catalog = (java.util.Hashtable) |
67 | Class.forName( "org.apache.derby.iapi.services.cache.ClassSizeCatalog").newInstance(); |
68 | } |
69 | catch( Exception e){}; |
70 | |
71 | // Figure out whether this is a 32 or 64 bit machine. |
72 | Runtime runtime = Runtime.getRuntime(); |
73 | long memBase = runtime.totalMemory() - runtime.freeMemory(); |
74 | Object[] junk = new Object[10000]; |
75 | long memUsed = runtime.totalMemory() - runtime.freeMemory() - memBase; |
76 | int sz = (int)((memUsed + junk.length/2)/junk.length); |
77 | refSize = ( 4 > sz) ? 4 : sz; |
78 | minObjectSize = 4*refSize; |
79 | } |
80 | |
81 | /** |
82 | * do not try to use the catalog. |
83 | */ |
84 | public static void setDummyCatalog() |
85 | { |
86 | dummyCatalog = true; |
87 | } |
88 | /** |
89 | * Get the estimate of the size of an object reference. |
90 | * |
91 | * @return the estimate in bytes. |
92 | */ |
93 | public static int getRefSize() |
94 | { |
95 | return refSize; |
96 | } |
97 | |
98 | /** |
99 | * @return the estimate of the size of a primitive int |
100 | */ |
101 | public static int getIntSize() |
102 | { |
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 | { |
122 | int[] coeff = {0, objectOverhead}; |
123 | |
124 | |
125 | |
126 | for( ; null != cl; cl = cl.getSuperclass()) |
127 | { |
128 | Field[] field = cl.getDeclaredFields(); |
129 | if( null != field) |
130 | { |
131 | for( int i = 0; i < field.length; i++) |
132 | { |
133 | if( ! Modifier.isStatic( field[i].getModifiers())) |
134 | { |
135 | Class fieldClass = field[i].getType(); |
136 | if( fieldClass.isArray() || ! fieldClass.isPrimitive()) |
137 | coeff[1]++; |
138 | else // Is simple primitive |
139 | { |
140 | String name = fieldClass.getName(); |
141 | |
142 | if( name.equals( "int") || name.equals( "I")) |
143 | coeff[0] += intSize; |
144 | else if( name.equals( "long") || name.equals( "J")) |
145 | coeff[0] += longSize; |
146 | else if( name.equals( "boolean") || name.equals( "Z")) |
147 | coeff[0] += booleanSize; |
148 | else if( name.equals( "short") || name.equals( "S")) |
149 | coeff[0] += shortSize; |
150 | else if( name.equals( "byte") || name.equals( "B")) |
151 | coeff[0] += 1; |
152 | else if( name.equals( "char") || name.equals( "C")) |
153 | coeff[0] += charSize; |
154 | else if( name.equals( "float") || name.equals( "F")) |
155 | coeff[0] += floatSize; |
156 | else if( name.equals( "double") || name.equals( "D")) |
157 | coeff[0] += doubleSize; |
158 | else // What is this?? |
159 | coeff[1]++; // Make a guess: one reference (?) |
160 | } |
161 | } |
162 | } |
163 | } |
164 | } |
165 | return coeff; |
166 | } // end of getSizeCoefficients |
167 | |
168 | /** |
169 | * Estimate the static space taken up by a class instance given the coefficients |
170 | * returned by getSizeCoefficients. |
171 | * |
172 | * @param coeff the coefficients |
173 | * |
174 | * @return the size estimate, in bytes |
175 | */ |
176 | public static int estimateBaseFromCoefficients( int[] coeff) |
177 | { |
178 | int size = coeff[0] + coeff[1]*refSize; |
179 | // Round up to a multiple of 8 |
180 | size = (size + 7)/8; |
181 | size *= 8; |
182 | return (size < minObjectSize) ? minObjectSize : size; |
183 | } // end of estimateBaseFromCoefficients |
184 | |
185 | /** |
186 | * Estimate the static space taken up by a class instance from cataloged coefficients. |
187 | * |
188 | * @param cls the class |
189 | * |
190 | * @return the size estimate, in bytes |
191 | * |
192 | * @see #estimateBaseFromCoefficients |
193 | * @see #getSizeCoefficients |
194 | * see org.apache.derbyBuild.ClassSizeCrawler |
195 | */ |
196 | public static int estimateBaseFromCatalog( Class cls) |
197 | { |
198 | return estimateBaseFromCatalog( cls, false); |
199 | } |
200 | |
201 | private static int estimateBaseFromCatalog( Class cls, boolean addToCatalog) |
202 | { |
203 | if( dummyCatalog) |
204 | return 0; |
205 | |
206 | if( SanityManager.DEBUG) |
207 | SanityManager.ASSERT( catalog != null, "The class size catalog could not be initialized."); |
208 | |
209 | int[] coeff = (int[]) catalog.get( cls.getName()); |
210 | if( coeff == null) |
211 | { |
212 | try |
213 | { |
214 | coeff = getSizeCoefficients( cls); |
215 | } |
216 | catch( Throwable t) |
217 | { |
218 | if( noGuess) |
219 | return -2; |
220 | coeff = wildGuess; |
221 | } |
222 | if( addToCatalog) |
223 | catalog.put( cls.getName(), coeff); |
224 | } |
225 | return estimateBaseFromCoefficients( coeff); |
226 | } // end of estimateBaseFromCatalog |
227 | |
228 | |
229 | /** |
230 | * Estimate the static space taken up by a class instance. Save the coefficients |
231 | * in a catalog. |
232 | * |
233 | * @param cls the class |
234 | * |
235 | * @return the size estimate, in bytes |
236 | * |
237 | * @see #estimateBaseFromCoefficients |
238 | * @see #getSizeCoefficients |
239 | * see org.apache.derbyBuild.ClassSizeCrawler |
240 | */ |
241 | public static int estimateAndCatalogBase( Class cls) |
242 | { |
243 | return estimateBaseFromCatalog( cls, true); |
244 | } // end of estimateAndCatalogBase |
245 | |
246 | /** |
247 | * Estimate the static space taken up by the fields of a class. This includes the space taken |
248 | * up by by references (the pointer) but not by the referenced object. So the estimated |
249 | * size of an array field does not depend on the size of the array. Similarly the size of |
250 | * an object (reference) field does not depend on the object. |
251 | * |
252 | * @return the size estimate in bytes. |
253 | * |
254 | * Note that this method will throw a SecurityException if the SecurityManager does not |
255 | * let this class execute the method Class.getDeclaredFields(). If this is a concern try |
256 | * to compute the size coefficients at build time. |
257 | * see org.apache.derbyBuild.ClassSizeCrawler |
258 | * @see #estimateBaseFromCatalog |
259 | */ |
260 | public static int estimateBase( Class cl) |
261 | { |
262 | return estimateBaseFromCoefficients( getSizeCoefficients( cl)); |
263 | } // End of estimateBase |
264 | |
265 | /** |
266 | * @return the estimated overhead of an array. The estimated size of an x[n] array is |
267 | * estimateArrayOverhead() + n*sizeOf(x). |
268 | */ |
269 | public static int estimateArrayOverhead() |
270 | { |
271 | return minObjectSize; |
272 | } |
273 | |
274 | /** |
275 | * Estimate the size of a Hashtable entry. In Java 1.2 we can use Map.entry, but this is not |
276 | * available in earlier versions of Java. |
277 | * |
278 | * @return the estimate, in bytes |
279 | */ |
280 | public static int estimateHashEntrySize() |
281 | { |
282 | return objectOverhead + 3*refSize; |
283 | } |
284 | |
285 | /** |
286 | * Estimate the size of a string. |
287 | * |
288 | * @return the estimated size, in bytes |
289 | */ |
290 | public static int estimateMemoryUsage( String str) |
291 | { |
292 | if( null == str) |
293 | return 0; |
294 | // Since Java uses Unicode assume that each character takes 2 bytes |
295 | return 2*str.length(); |
296 | } |
297 | } |