1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.util;
22
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Modifier;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentSkipListMap;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.hbase.classification.InterfaceAudience;
31
32
33
34
35
36
37
38 @InterfaceAudience.Private
39 public class ClassSize {
40 private static final Log LOG = LogFactory.getLog(ClassSize.class);
41
42
43 public static final int ARRAY;
44
45
46 public static final int ARRAYLIST;
47
48
49 public static final int BYTE_BUFFER;
50
51
52 public static final int INTEGER;
53
54
55 public static final int MAP_ENTRY;
56
57
58 public static final int OBJECT;
59
60
61 public static final int REFERENCE;
62
63
64 public static final int STRING;
65
66
67 public static final int TREEMAP;
68
69
70 public static final int CONCURRENT_HASHMAP;
71
72
73 public static final int CONCURRENT_HASHMAP_ENTRY;
74
75
76 public static final int CONCURRENT_HASHMAP_SEGMENT;
77
78
79 public static final int CONCURRENT_SKIPLISTMAP;
80
81
82 public static final int CONCURRENT_SKIPLISTMAP_ENTRY;
83
84
85 public static final int REENTRANT_LOCK;
86
87
88 public static final int ATOMIC_LONG;
89
90
91 public static final int ATOMIC_INTEGER;
92
93
94 public static final int ATOMIC_BOOLEAN;
95
96
97 public static final int COPYONWRITE_ARRAYSET;
98
99
100 public static final int COPYONWRITE_ARRAYLIST;
101
102
103 public static final int TIMERANGE;
104
105
106 public static final int TIMERANGE_TRACKER;
107
108
109 public static final int CELL_SKIPLIST_SET;
110
111
112
113
114
115 private static class MemoryLayout {
116 int headerSize() {
117 return 2 * oopSize();
118 }
119
120 int arrayHeaderSize() {
121 return (int) align(3 * oopSize());
122 }
123
124
125
126
127
128 int oopSize() {
129 return is32BitJVM() ? 4 : 8;
130 }
131
132
133
134
135
136
137 public long align(long num) {
138
139
140 return ((num + 7) >> 3) << 3;
141 }
142
143 long sizeOf(byte[] b, int len) {
144 return align(ARRAY + len);
145 }
146 }
147
148
149
150
151
152
153 private static class UnsafeLayout extends MemoryLayout {
154 @SuppressWarnings("unused")
155 private static final class HeaderSize {
156 private byte a;
157 }
158
159 public UnsafeLayout() {
160 }
161
162 @Override
163 int headerSize() {
164 try {
165 return (int) UnsafeAccess.theUnsafe.objectFieldOffset(
166 HeaderSize.class.getDeclaredField("a"));
167 } catch (NoSuchFieldException | SecurityException e) {
168 LOG.error(e);
169 }
170 return super.headerSize();
171 }
172
173 @Override
174 int arrayHeaderSize() {
175 return UnsafeAccess.theUnsafe.arrayBaseOffset(byte[].class);
176 }
177
178 @Override
179 @SuppressWarnings("static-access")
180 int oopSize() {
181
182
183 return UnsafeAccess.theUnsafe.ARRAY_OBJECT_INDEX_SCALE;
184 }
185
186 @Override
187 @SuppressWarnings("static-access")
188 long sizeOf(byte[] b, int len) {
189 return align(ARRAY + len * UnsafeAccess.theUnsafe.ARRAY_BYTE_INDEX_SCALE);
190 }
191 }
192
193 private static MemoryLayout getMemoryLayout() {
194
195
196 String enabled = System.getProperty("hbase.memorylayout.use.unsafe");
197 if (UnsafeAvailChecker.isAvailable() && (enabled == null || Boolean.parseBoolean(enabled))) {
198 LOG.debug("Using Unsafe to estimate memory layout");
199 return new UnsafeLayout();
200 }
201 LOG.debug("Not using Unsafe to estimate memory layout");
202 return new MemoryLayout();
203 }
204
205 private static final MemoryLayout memoryLayout = getMemoryLayout();
206
207
208
209
210
211 static {
212 REFERENCE = memoryLayout.oopSize();
213
214 OBJECT = memoryLayout.headerSize();
215
216 ARRAY = memoryLayout.arrayHeaderSize();
217
218 ARRAYLIST = align(OBJECT + REFERENCE + (2 * Bytes.SIZEOF_INT)) + align(ARRAY);
219
220
221 BYTE_BUFFER = align(OBJECT + REFERENCE +
222 (5 * Bytes.SIZEOF_INT) +
223 (3 * Bytes.SIZEOF_BOOLEAN) + Bytes.SIZEOF_LONG) + align(ARRAY);
224
225 INTEGER = align(OBJECT + Bytes.SIZEOF_INT);
226
227 MAP_ENTRY = align(OBJECT + 5 * REFERENCE + Bytes.SIZEOF_BOOLEAN);
228
229 TREEMAP = align(OBJECT + (2 * Bytes.SIZEOF_INT) + 7 * REFERENCE);
230
231
232
233 STRING = (int) estimateBase(String.class, false);
234
235
236
237
238 CONCURRENT_HASHMAP = (int) estimateBase(ConcurrentHashMap.class, false);
239
240 CONCURRENT_HASHMAP_ENTRY = align(REFERENCE + OBJECT + (3 * REFERENCE) +
241 (2 * Bytes.SIZEOF_INT));
242
243 CONCURRENT_HASHMAP_SEGMENT = align(REFERENCE + OBJECT +
244 (3 * Bytes.SIZEOF_INT) + Bytes.SIZEOF_FLOAT + ARRAY);
245
246
247 CONCURRENT_SKIPLISTMAP = (int) estimateBase(ConcurrentSkipListMap.class, false);
248
249 CONCURRENT_SKIPLISTMAP_ENTRY =
250 align(OBJECT + (3 * REFERENCE)) +
251 align((OBJECT + (3 * REFERENCE))/2);
252
253 REENTRANT_LOCK = align(OBJECT + (3 * REFERENCE));
254
255 ATOMIC_LONG = align(OBJECT + Bytes.SIZEOF_LONG);
256
257 ATOMIC_INTEGER = align(OBJECT + Bytes.SIZEOF_INT);
258
259 ATOMIC_BOOLEAN = align(OBJECT + Bytes.SIZEOF_BOOLEAN);
260
261 COPYONWRITE_ARRAYSET = align(OBJECT + REFERENCE);
262
263 COPYONWRITE_ARRAYLIST = align(OBJECT + (2 * REFERENCE) + ARRAY);
264
265 TIMERANGE = align(ClassSize.OBJECT + Bytes.SIZEOF_LONG * 2 + Bytes.SIZEOF_BOOLEAN);
266
267 TIMERANGE_TRACKER = align(ClassSize.OBJECT + 2 * REFERENCE);
268
269 CELL_SKIPLIST_SET = align(OBJECT + REFERENCE);
270 }
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287 @SuppressWarnings("unchecked")
288 private static int [] getSizeCoefficients(Class cl, boolean debug) {
289 int primitives = 0;
290 int arrays = 0;
291 int references = 0;
292 int index = 0;
293
294 for ( ; null != cl; cl = cl.getSuperclass()) {
295 Field[] field = cl.getDeclaredFields();
296 if (null != field) {
297 for (Field aField : field) {
298 if (Modifier.isStatic(aField.getModifiers())) continue;
299 Class fieldClass = aField.getType();
300 if (fieldClass.isArray()) {
301 arrays++;
302 references++;
303 } else if (!fieldClass.isPrimitive()) {
304 references++;
305 } else {
306 String name = fieldClass.getName();
307
308 if (name.equals("int") || name.equals("I"))
309 primitives += Bytes.SIZEOF_INT;
310 else if (name.equals("long") || name.equals("J"))
311 primitives += Bytes.SIZEOF_LONG;
312 else if (name.equals("boolean") || name.equals("Z"))
313 primitives += Bytes.SIZEOF_BOOLEAN;
314 else if (name.equals("short") || name.equals("S"))
315 primitives += Bytes.SIZEOF_SHORT;
316 else if (name.equals("byte") || name.equals("B"))
317 primitives += Bytes.SIZEOF_BYTE;
318 else if (name.equals("char") || name.equals("C"))
319 primitives += Bytes.SIZEOF_CHAR;
320 else if (name.equals("float") || name.equals("F"))
321 primitives += Bytes.SIZEOF_FLOAT;
322 else if (name.equals("double") || name.equals("D"))
323 primitives += Bytes.SIZEOF_DOUBLE;
324 }
325 if (debug) {
326 if (LOG.isDebugEnabled()) {
327 LOG.debug("" + index + " " + aField.getName() + " " + aField.getType());
328 }
329 }
330 index++;
331 }
332 }
333 }
334 return new int [] {primitives, arrays, references};
335 }
336
337
338
339
340
341
342
343
344
345
346 private static long estimateBaseFromCoefficients(int [] coeff, boolean debug) {
347 long prealign_size = (long) OBJECT + coeff[0] + coeff[2] * REFERENCE;
348
349
350 long size = align(prealign_size) + align(coeff[1] * ARRAY);
351 if (debug) {
352 if (LOG.isDebugEnabled()) {
353 LOG.debug("Primitives=" + coeff[0] + ", arrays=" + coeff[1] +
354 ", references=" + coeff[2] + ", refSize " + REFERENCE +
355 ", size=" + size + ", prealign_size=" + prealign_size);
356 }
357 }
358 return size;
359 }
360
361
362
363
364
365
366
367
368
369
370
371
372 @SuppressWarnings("unchecked")
373 public static long estimateBase(Class cl, boolean debug) {
374 return estimateBaseFromCoefficients( getSizeCoefficients(cl, debug), debug);
375 }
376
377
378
379
380
381
382 public static int align(int num) {
383 return (int)(align((long)num));
384 }
385
386
387
388
389
390
391 public static long align(long num) {
392 return memoryLayout.align(num);
393 }
394
395
396
397
398
399 public static boolean is32BitJVM() {
400 final String model = System.getProperty("sun.arch.data.model");
401 return model != null && model.equals("32");
402 }
403
404 public static long sizeOf(byte[] b, int len) {
405 return memoryLayout.sizeOf(b, len);
406 }
407
408 }
409