1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.accumulo.core.file.blockfile.cache;
22
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Modifier;
25 import java.util.Properties;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 /**
31 * Class for determining the "size" of a class, an attempt to calculate the actual bytes that an object of this class will occupy in memory
32 *
33 * The core of this class is taken from the Derby project
34 */
35 public class ClassSize {
36 static final Log LOG = LogFactory.getLog(ClassSize.class);
37
38 private static int nrOfRefsPerObj = 2;
39
40 /** Array overhead */
41 public static int ARRAY = 0;
42
43 /** Overhead for ArrayList(0) */
44 public static int ARRAYLIST = 0;
45
46 /** Overhead for ByteBuffer */
47 public static int BYTE_BUFFER = 0;
48
49 /** Overhead for an Integer */
50 public static int INTEGER = 0;
51
52 /** Overhead for entry in map */
53 public static int MAP_ENTRY = 0;
54
55 /** Object overhead is minimum 2 * reference size (8 bytes on 64-bit) */
56 public static int OBJECT = 0;
57
58 /** Reference size is 8 bytes on 64-bit, 4 bytes on 32-bit */
59 public static int REFERENCE = 0;
60
61 /** String overhead */
62 public static int STRING = 0;
63
64 /** Overhead for TreeMap */
65 public static int TREEMAP = 0;
66
67 /** Overhead for ConcurrentHashMap */
68 public static int CONCURRENT_HASHMAP = 0;
69
70 /** Overhead for ConcurrentHashMap.Entry */
71 public static int CONCURRENT_HASHMAP_ENTRY = 0;
72
73 /** Overhead for ConcurrentHashMap.Segment */
74 public static int CONCURRENT_HASHMAP_SEGMENT = 0;
75
76 /** Overhead for ConcurrentSkipListMap */
77 public static int CONCURRENT_SKIPLISTMAP = 0;
78
79 /** Overhead for ConcurrentSkipListMap Entry */
80 public static int CONCURRENT_SKIPLISTMAP_ENTRY = 0;
81
82 /** Overhead for ReentrantReadWriteLock */
83 public static int REENTRANT_LOCK = 0;
84
85 /** Overhead for AtomicLong */
86 public static int ATOMIC_LONG = 0;
87
88 /** Overhead for AtomicInteger */
89 public static int ATOMIC_INTEGER = 0;
90
91 /** Overhead for AtomicBoolean */
92 public static int ATOMIC_BOOLEAN = 0;
93
94 /** Overhead for CopyOnWriteArraySet */
95 public static int COPYONWRITE_ARRAYSET = 0;
96
97 /** Overhead for CopyOnWriteArrayList */
98 public static int COPYONWRITE_ARRAYLIST = 0;
99
100 private static final String THIRTY_TWO = "32";
101
102 /**
103 * Method for reading the arc settings and setting overheads according to 32-bit or 64-bit architecture.
104 */
105 static {
106
107 Properties sysProps = System.getProperties();
108 String arcModel = sysProps.getProperty("sun.arch.data.model");
109
110
111 REFERENCE = 8;
112 if (arcModel.equals(THIRTY_TWO)) {
113 REFERENCE = 4;
114 }
115
116 OBJECT = 2 * REFERENCE;
117
118 ARRAY = 3 * REFERENCE;
119
120 ARRAYLIST = align(OBJECT + align(REFERENCE) + align(ARRAY) + (2 * SizeConstants.SIZEOF_INT));
121
122 BYTE_BUFFER = align(OBJECT + align(REFERENCE) + align(ARRAY) + (5 * SizeConstants.SIZEOF_INT) + (3 * SizeConstants.SIZEOF_BOOLEAN)
123 + SizeConstants.SIZEOF_LONG);
124
125 INTEGER = align(OBJECT + SizeConstants.SIZEOF_INT);
126
127 MAP_ENTRY = align(OBJECT + 5 * REFERENCE + SizeConstants.SIZEOF_BOOLEAN);
128
129 TREEMAP = align(OBJECT + (2 * SizeConstants.SIZEOF_INT) + align(7 * REFERENCE));
130
131 STRING = align(OBJECT + ARRAY + REFERENCE + 3 * SizeConstants.SIZEOF_INT);
132
133 CONCURRENT_HASHMAP = align((2 * SizeConstants.SIZEOF_INT) + ARRAY + (6 * REFERENCE) + OBJECT);
134
135 CONCURRENT_HASHMAP_ENTRY = align(REFERENCE + OBJECT + (3 * REFERENCE) + (2 * SizeConstants.SIZEOF_INT));
136
137 CONCURRENT_HASHMAP_SEGMENT = align(REFERENCE + OBJECT + (3 * SizeConstants.SIZEOF_INT) + SizeConstants.SIZEOF_FLOAT + ARRAY);
138
139 CONCURRENT_SKIPLISTMAP = align(SizeConstants.SIZEOF_INT + OBJECT + (8 * REFERENCE));
140
141 CONCURRENT_SKIPLISTMAP_ENTRY = align(align(OBJECT + (3 * REFERENCE)) +
142 align((OBJECT + (3 * REFERENCE)) / 2));
143
144 REENTRANT_LOCK = align(OBJECT + (3 * REFERENCE));
145
146 ATOMIC_LONG = align(OBJECT + SizeConstants.SIZEOF_LONG);
147
148 ATOMIC_INTEGER = align(OBJECT + SizeConstants.SIZEOF_INT);
149
150 ATOMIC_BOOLEAN = align(OBJECT + SizeConstants.SIZEOF_BOOLEAN);
151
152 COPYONWRITE_ARRAYSET = align(OBJECT + REFERENCE);
153
154 COPYONWRITE_ARRAYLIST = align(OBJECT + (2 * REFERENCE) + ARRAY);
155 }
156
157 /**
158 * The estimate of the size of a class instance depends on whether the JVM uses 32 or 64 bit addresses, that is it depends on the size of an object reference.
159 * It is a linear function of the size of a reference, e.g. 24 + 5*r where r is the size of a reference (usually 4 or 8 bytes).
160 *
161 * This method returns the coefficients of the linear function, e.g. {24, 5} in the above example.
162 *
163 * @param cl
164 * A class whose instance size is to be estimated
165 * @return an array of 3 integers. The first integer is the size of the primitives, the second the number of arrays and the third the number of references.
166 */
167 private static int[] getSizeCoefficients(Class<?> cl, boolean debug) {
168 int primitives = 0;
169 int arrays = 0;
170
171 int references = nrOfRefsPerObj;
172
173 for (; null != cl; cl = cl.getSuperclass()) {
174 Field[] field = cl.getDeclaredFields();
175 if (null != field) {
176 for (int i = 0; i < field.length; i++) {
177 if (!Modifier.isStatic(field[i].getModifiers())) {
178 Class<?> fieldClass = field[i].getType();
179 if (fieldClass.isArray()) {
180 arrays++;
181 references++;
182 } else if (!fieldClass.isPrimitive()) {
183 references++;
184 } else {
185 String name = fieldClass.getName();
186
187 if (name.equals("int") || name.equals("I"))
188 primitives += SizeConstants.SIZEOF_INT;
189 else if (name.equals("long") || name.equals("J"))
190 primitives += SizeConstants.SIZEOF_LONG;
191 else if (name.equals("boolean") || name.equals("Z"))
192 primitives += SizeConstants.SIZEOF_BOOLEAN;
193 else if (name.equals("short") || name.equals("S"))
194 primitives += SizeConstants.SIZEOF_SHORT;
195 else if (name.equals("byte") || name.equals("B"))
196 primitives += SizeConstants.SIZEOF_BYTE;
197 else if (name.equals("char") || name.equals("C"))
198 primitives += SizeConstants.SIZEOF_CHAR;
199 else if (name.equals("float") || name.equals("F"))
200 primitives += SizeConstants.SIZEOF_FLOAT;
201 else if (name.equals("double") || name.equals("D"))
202 primitives += SizeConstants.SIZEOF_DOUBLE;
203 }
204 if (debug) {
205 if (LOG.isDebugEnabled()) {
206
207 LOG.debug(field[i].getName() + "\n\t" + field[i].getType());
208 }
209 }
210 }
211 }
212 }
213 }
214 return new int[] {primitives, arrays, references};
215 }
216
217 /**
218 * Estimate the static space taken up by a class instance given the coefficients returned by getSizeCoefficients.
219 *
220 * @param coeff
221 * the coefficients
222 *
223 * @return the size estimate, in bytes
224 */
225 private static long estimateBaseFromCoefficients(int[] coeff, boolean debug) {
226 long size = coeff[0] + align(coeff[1] * ARRAY) + coeff[2] * REFERENCE;
227
228
229 size = align(size);
230 if (debug) {
231 if (LOG.isDebugEnabled()) {
232
233 LOG.debug("Primitives " + coeff[0] + ", arrays " + coeff[1] + ", references(includes " + nrOfRefsPerObj + " for object overhead) " + coeff[2]
234 + ", refSize " + REFERENCE + ", size " + size);
235 }
236 }
237 return size;
238 }
239
240 /**
241 * Estimate the static space taken up by the fields of a class. This includes the space taken up by by references (the pointer) but not by the referenced
242 * object. So the estimated size of an array field does not depend on the size of the array. Similarly the size of an object (reference) field does not depend
243 * on the object.
244 *
245 * @return the size estimate in bytes.
246 */
247 public static long estimateBase(Class<?> cl, boolean debug) {
248 return estimateBaseFromCoefficients(getSizeCoefficients(cl, debug), debug);
249 }
250
251 /**
252 * Aligns a number to 8.
253 *
254 * @param num
255 * number to align to 8
256 * @return smallest number >= input that is a multiple of 8
257 */
258 public static int align(int num) {
259 return (int) (align((long) num));
260 }
261
262 /**
263 * Aligns a number to 8.
264 *
265 * @param num
266 * number to align to 8
267 * @return smallest number >= input that is a multiple of 8
268 */
269 public static long align(long num) {
270
271
272 return ((num + 7) >> 3) << 3;
273 }
274
275 }