001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.io;
019
020import static org.hamcrest.MatcherAssert.assertThat;
021import static org.hamcrest.Matchers.lessThan;
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertTrue;
024
025import java.io.IOException;
026import java.lang.management.ManagementFactory;
027import java.lang.management.RuntimeMXBean;
028import java.nio.ByteBuffer;
029import java.util.ArrayList;
030import java.util.Arrays;
031import java.util.LinkedList;
032import java.util.List;
033import java.util.Map;
034import java.util.TreeMap;
035import java.util.concurrent.ConcurrentHashMap;
036import java.util.concurrent.ConcurrentSkipListMap;
037import java.util.concurrent.CopyOnWriteArrayList;
038import java.util.concurrent.CopyOnWriteArraySet;
039import java.util.concurrent.TimeUnit;
040import java.util.concurrent.atomic.AtomicBoolean;
041import java.util.concurrent.atomic.AtomicInteger;
042import java.util.concurrent.atomic.AtomicLong;
043import java.util.concurrent.atomic.AtomicReference;
044import java.util.concurrent.locks.ReentrantReadWriteLock;
045import org.apache.hadoop.hbase.HBaseClassTestRule;
046import org.apache.hadoop.hbase.KeyValue;
047import org.apache.hadoop.hbase.client.Delete;
048import org.apache.hadoop.hbase.client.Mutation;
049import org.apache.hadoop.hbase.client.Put;
050import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
051import org.apache.hadoop.hbase.io.hfile.ExclusiveMemHFileBlock;
052import org.apache.hadoop.hbase.io.hfile.HFileBlock;
053import org.apache.hadoop.hbase.io.hfile.HFileContext;
054import org.apache.hadoop.hbase.io.hfile.LruBlockCache;
055import org.apache.hadoop.hbase.io.hfile.LruCachedBlock;
056import org.apache.hadoop.hbase.io.hfile.SharedMemHFileBlock;
057import org.apache.hadoop.hbase.regionserver.CSLMImmutableSegment;
058import org.apache.hadoop.hbase.regionserver.CellArrayImmutableSegment;
059import org.apache.hadoop.hbase.regionserver.CellArrayMap;
060import org.apache.hadoop.hbase.regionserver.CellSet;
061import org.apache.hadoop.hbase.regionserver.CompactingMemStore;
062import org.apache.hadoop.hbase.regionserver.CompactionPipeline;
063import org.apache.hadoop.hbase.regionserver.DefaultMemStore;
064import org.apache.hadoop.hbase.regionserver.HRegion;
065import org.apache.hadoop.hbase.regionserver.HStore;
066import org.apache.hadoop.hbase.regionserver.ImmutableSegment;
067import org.apache.hadoop.hbase.regionserver.MemStoreCompactor;
068import org.apache.hadoop.hbase.regionserver.MutableSegment;
069import org.apache.hadoop.hbase.regionserver.Segment;
070import org.apache.hadoop.hbase.regionserver.StoreContext;
071import org.apache.hadoop.hbase.regionserver.TimeRangeTracker.NonSyncTimeRangeTracker;
072import org.apache.hadoop.hbase.regionserver.TimeRangeTracker.SyncTimeRangeTracker;
073import org.apache.hadoop.hbase.regionserver.throttle.StoreHotnessProtector;
074import org.apache.hadoop.hbase.testclassification.IOTests;
075import org.apache.hadoop.hbase.testclassification.SmallTests;
076import org.apache.hadoop.hbase.util.ClassSize;
077import org.junit.BeforeClass;
078import org.junit.ClassRule;
079import org.junit.Test;
080import org.junit.experimental.categories.Category;
081import org.slf4j.Logger;
082import org.slf4j.LoggerFactory;
083
084/**
085 * Testing the sizing that HeapSize offers and compares to the size given by ClassSize.
086 */
087@Category({ IOTests.class, SmallTests.class })
088public class TestHeapSize {
089
090  @ClassRule
091  public static final HBaseClassTestRule CLASS_RULE =
092    HBaseClassTestRule.forClass(TestHeapSize.class);
093
094  private static final Logger LOG = LoggerFactory.getLogger(TestHeapSize.class);
095  // List of classes implementing HeapSize
096  // BatchOperation, BatchUpdate, BlockIndex, Entry, Entry<K,V>, HStoreKey
097  // KeyValue, LruBlockCache, Put, WALKey
098
099  @BeforeClass
100  public static void beforeClass() throws Exception {
101    // Print detail on jvm so we know what is different should below test fail.
102    RuntimeMXBean b = ManagementFactory.getRuntimeMXBean();
103    LOG.info("name=" + b.getName());
104    LOG.info("specname=" + b.getSpecName());
105    LOG.info("specvendor=" + b.getSpecVendor());
106    LOG.info("vmname=" + b.getVmName());
107    LOG.info("vmversion=" + b.getVmVersion());
108    LOG.info("vmvendor=" + b.getVmVendor());
109    Map<String, String> p = b.getSystemProperties();
110    LOG.info("properties=" + p);
111  }
112
113  /**
114   * Test our hard-coded sizing of native java objects
115   */
116  @Test
117  public void testNativeSizes() throws IOException {
118    Class<?> cl;
119    long expected;
120    long actual;
121
122    // ArrayList
123    cl = ArrayList.class;
124    expected = ClassSize.estimateBase(cl, false);
125    actual = ClassSize.ARRAYLIST;
126    if (expected != actual) {
127      ClassSize.estimateBase(cl, true);
128      assertEquals(expected, actual);
129    }
130
131    // ByteBuffer
132    cl = ByteBuffer.class;
133    expected = ClassSize.estimateBase(cl, false);
134    actual = ClassSize.BYTE_BUFFER;
135    if (expected != actual) {
136      ClassSize.estimateBase(cl, true);
137      assertEquals(expected, actual);
138    }
139
140    // Integer
141    cl = Integer.class;
142    expected = ClassSize.estimateBase(cl, false);
143    actual = ClassSize.INTEGER;
144    if (expected != actual) {
145      ClassSize.estimateBase(cl, true);
146      assertEquals(expected, actual);
147    }
148
149    // Map.Entry
150    // Interface is public, all others are not. Hard to size via ClassSize
151    // cl = Map.Entry.class;
152    // expected = ClassSize.estimateBase(cl, false);
153    // actual = ClassSize.MAP_ENTRY;
154    // if(expected != actual) {
155    // ClassSize.estimateBase(cl, true);
156    // assertEquals(expected, actual);
157    // }
158
159    // Object
160    cl = Object.class;
161    expected = ClassSize.estimateBase(cl, false);
162    actual = ClassSize.align(ClassSize.OBJECT);
163    if (expected != actual) {
164      ClassSize.estimateBase(cl, true);
165      assertEquals(expected, actual);
166    }
167
168    // TreeMap
169    cl = TreeMap.class;
170    expected = ClassSize.estimateBase(cl, false);
171    actual = ClassSize.TREEMAP;
172    if (expected != actual) {
173      ClassSize.estimateBase(cl, true);
174      assertEquals(expected, actual);
175    }
176
177    // String
178    cl = String.class;
179    expected = ClassSize.estimateBase(cl, false);
180    actual = ClassSize.STRING;
181    if (expected != actual) {
182      ClassSize.estimateBase(cl, true);
183      assertEquals(expected, actual);
184    }
185
186    // ConcurrentHashMap
187    cl = ConcurrentHashMap.class;
188    expected = ClassSize.estimateBase(cl, false);
189    actual = ClassSize.CONCURRENT_HASHMAP;
190    if (expected != actual) {
191      ClassSize.estimateBase(cl, true);
192      assertEquals(expected, actual);
193    }
194
195    // ConcurrentSkipListMap
196    cl = ConcurrentSkipListMap.class;
197    expected = ClassSize.estimateBase(cl, false);
198    actual = ClassSize.CONCURRENT_SKIPLISTMAP;
199    if (expected != actual) {
200      ClassSize.estimateBase(cl, true);
201      assertEquals(expected, actual);
202    }
203
204    // CellArrayMap
205    cl = CellArrayMap.class;
206    expected = ClassSize.estimateBase(cl, false);
207    actual = ClassSize.CELL_ARRAY_MAP;
208    if (expected != actual) {
209      ClassSize.estimateBase(cl, true);
210      assertEquals(expected, actual);
211    }
212
213    // ReentrantReadWriteLock
214    cl = ReentrantReadWriteLock.class;
215    expected = ClassSize.estimateBase(cl, false);
216    actual = ClassSize.REENTRANT_LOCK;
217    if (expected != actual) {
218      ClassSize.estimateBase(cl, true);
219      assertEquals(expected, actual);
220    }
221
222    // AtomicLong
223    cl = AtomicLong.class;
224    expected = ClassSize.estimateBase(cl, false);
225    actual = ClassSize.ATOMIC_LONG;
226    if (expected != actual) {
227      ClassSize.estimateBase(cl, true);
228      assertEquals(expected, actual);
229    }
230
231    // AtomicInteger
232    cl = AtomicInteger.class;
233    expected = ClassSize.estimateBase(cl, false);
234    actual = ClassSize.ATOMIC_INTEGER;
235    if (expected != actual) {
236      ClassSize.estimateBase(cl, true);
237      assertEquals(expected, actual);
238    }
239
240    // AtomicBoolean
241    cl = AtomicBoolean.class;
242    expected = ClassSize.estimateBase(cl, false);
243    actual = ClassSize.ATOMIC_BOOLEAN;
244    if (expected != actual) {
245      ClassSize.estimateBase(cl, true);
246      assertEquals(expected, actual);
247    }
248
249    // CopyOnWriteArraySet
250    cl = CopyOnWriteArraySet.class;
251    expected = ClassSize.estimateBase(cl, false);
252    actual = ClassSize.COPYONWRITE_ARRAYSET;
253    if (expected != actual) {
254      ClassSize.estimateBase(cl, true);
255      assertEquals(expected, actual);
256    }
257
258    // CopyOnWriteArrayList
259    cl = CopyOnWriteArrayList.class;
260    expected = ClassSize.estimateBase(cl, false);
261    actual = ClassSize.COPYONWRITE_ARRAYLIST;
262    if (expected != actual) {
263      ClassSize.estimateBase(cl, true);
264      assertEquals(expected, actual);
265    }
266
267    // SyncTimeRangeTracker
268    cl = SyncTimeRangeTracker.class;
269    expected = ClassSize.estimateBase(cl, false);
270    actual = ClassSize.SYNC_TIMERANGE_TRACKER;
271    if (expected != actual) {
272      ClassSize.estimateBase(cl, true);
273      assertEquals(expected, actual);
274    }
275
276    // NonSyncTimeRangeTracker
277    cl = NonSyncTimeRangeTracker.class;
278    expected = ClassSize.estimateBase(cl, false);
279    actual = ClassSize.NON_SYNC_TIMERANGE_TRACKER;
280    if (expected != actual) {
281      ClassSize.estimateBase(cl, true);
282      assertEquals(expected, actual);
283    }
284
285    // CellSet
286    cl = CellSet.class;
287    expected = ClassSize.estimateBase(cl, false);
288    actual = ClassSize.CELL_SET;
289    if (expected != actual) {
290      ClassSize.estimateBase(cl, true);
291      assertEquals(expected, actual);
292    }
293  }
294
295  /**
296   * Testing the classes that implements HeapSize and are a part of 0.20. Some are not tested here
297   * for example BlockIndex which is tested in TestHFile since it is a non public class
298   */
299  @Test
300  public void testSizes() throws IOException {
301    Class<?> cl;
302    long expected;
303    long actual;
304
305    // KeyValue
306    cl = KeyValue.class;
307    expected = ClassSize.estimateBase(cl, false);
308    KeyValue kv = new KeyValue();
309    actual = kv.heapSize();
310    if (expected != actual) {
311      ClassSize.estimateBase(cl, true);
312      assertEquals(expected, actual);
313    }
314
315    // LruBlockCache Overhead
316    cl = LruBlockCache.class;
317    actual = LruBlockCache.CACHE_FIXED_OVERHEAD;
318    expected = ClassSize.estimateBase(cl, false);
319    if (expected != actual) {
320      ClassSize.estimateBase(cl, true);
321      assertEquals(expected, actual);
322    }
323
324    // CachedBlock Fixed Overhead
325    // We really need "deep" sizing but ClassSize does not do this.
326    // Perhaps we should do all these more in this style....
327    cl = LruCachedBlock.class;
328    actual = LruCachedBlock.PER_BLOCK_OVERHEAD;
329    expected = ClassSize.estimateBase(cl, false);
330    expected += ClassSize.estimateBase(String.class, false);
331    expected += ClassSize.estimateBase(ByteBuffer.class, false);
332    if (expected != actual) {
333      ClassSize.estimateBase(cl, true);
334      ClassSize.estimateBase(String.class, true);
335      ClassSize.estimateBase(ByteBuffer.class, true);
336      assertEquals(expected, actual);
337    }
338
339    // DefaultMemStore Overhead
340    cl = DefaultMemStore.class;
341    actual = DefaultMemStore.FIXED_OVERHEAD;
342    expected = ClassSize.estimateBase(cl, false);
343    if (expected != actual) {
344      ClassSize.estimateBase(cl, true);
345      assertEquals(expected, actual);
346    }
347
348    // DefaultMemStore Deep Overhead
349    actual = DefaultMemStore.DEEP_OVERHEAD;
350    expected = ClassSize.estimateBase(cl, false);
351    if (expected != actual) {
352      ClassSize.estimateBase(cl, true);
353      assertEquals(expected, actual);
354    }
355
356    // CompactingMemStore Deep Overhead
357    cl = CompactingMemStore.class;
358    actual = CompactingMemStore.DEEP_OVERHEAD;
359    expected = ClassSize.estimateBase(cl, false);
360    expected += ClassSize.estimateBase(AtomicBoolean.class, false);
361    expected += ClassSize.estimateBase(AtomicBoolean.class, false);
362    expected += ClassSize.estimateBase(CompactionPipeline.class, false);
363    expected += ClassSize.estimateBase(LinkedList.class, false); // inside CompactionPipeline
364    expected += ClassSize.estimateBase(LinkedList.class, false); // inside CompactionPipeline
365    expected += ClassSize.estimateBase(MemStoreCompactor.class, false);
366    expected += ClassSize.estimateBase(AtomicBoolean.class, false);// inside MemStoreCompactor
367    if (expected != actual) {
368      ClassSize.estimateBase(cl, true);
369      ClassSize.estimateBase(AtomicBoolean.class, true);
370      ClassSize.estimateBase(AtomicBoolean.class, true);
371      ClassSize.estimateBase(CompactionPipeline.class, true);
372      ClassSize.estimateBase(LinkedList.class, true);
373      ClassSize.estimateBase(LinkedList.class, true);
374      ClassSize.estimateBase(MemStoreCompactor.class, true);
375      ClassSize.estimateBase(AtomicBoolean.class, true);
376      assertEquals(expected, actual);
377    }
378
379    // Segment Deep overhead
380    cl = Segment.class;
381    actual = Segment.DEEP_OVERHEAD;
382    expected = ClassSize.estimateBase(cl, false);
383    expected += 2 * ClassSize.estimateBase(AtomicLong.class, false);
384    expected += ClassSize.estimateBase(AtomicReference.class, false);
385    expected += ClassSize.estimateBase(CellSet.class, false);
386    expected += ClassSize.estimateBase(ReentrantReadWriteLock.class, false);
387    if (expected != actual) {
388      ClassSize.estimateBase(cl, true);
389      ClassSize.estimateBase(AtomicLong.class, true);
390      ClassSize.estimateBase(AtomicReference.class, true);
391      ClassSize.estimateBase(CellSet.class, true);
392      ClassSize.estimateBase(ReentrantReadWriteLock.class, true);
393      assertEquals(expected, actual);
394    }
395
396    // MutableSegment Deep overhead
397    cl = MutableSegment.class;
398    actual = MutableSegment.DEEP_OVERHEAD;
399    expected = ClassSize.estimateBase(cl, false);
400    expected += 2 * ClassSize.estimateBase(AtomicLong.class, false);
401    expected += ClassSize.estimateBase(AtomicReference.class, false);
402    expected += ClassSize.estimateBase(CellSet.class, false);
403    expected += ClassSize.estimateBase(ReentrantReadWriteLock.class, false);
404    expected += ClassSize.estimateBase(SyncTimeRangeTracker.class, false);
405    expected += ClassSize.estimateBase(ConcurrentSkipListMap.class, false);
406    expected += ClassSize.estimateBase(AtomicBoolean.class, false);
407    if (expected != actual) {
408      ClassSize.estimateBase(cl, true);
409      ClassSize.estimateBase(AtomicLong.class, true);
410      ClassSize.estimateBase(AtomicLong.class, true);
411      ClassSize.estimateBase(AtomicReference.class, true);
412      ClassSize.estimateBase(CellSet.class, true);
413      ClassSize.estimateBase(ReentrantReadWriteLock.class, true);
414      ClassSize.estimateBase(SyncTimeRangeTracker.class, true);
415      ClassSize.estimateBase(ConcurrentSkipListMap.class, true);
416      ClassSize.estimateBase(AtomicBoolean.class, true);
417      assertEquals(expected, actual);
418    }
419
420    // ImmutableSegments Deep overhead
421    cl = ImmutableSegment.class;
422    actual = ImmutableSegment.DEEP_OVERHEAD;
423    expected = ClassSize.estimateBase(cl, false);
424    expected += 2 * ClassSize.estimateBase(AtomicLong.class, false);
425    expected += ClassSize.estimateBase(AtomicReference.class, false);
426    expected += ClassSize.estimateBase(CellSet.class, false);
427    expected += ClassSize.estimateBase(ReentrantReadWriteLock.class, false);
428    expected += ClassSize.estimateBase(NonSyncTimeRangeTracker.class, false);
429    if (expected != actual) {
430      ClassSize.estimateBase(cl, true);
431      ClassSize.estimateBase(AtomicLong.class, true);
432      ClassSize.estimateBase(AtomicLong.class, true);
433      ClassSize.estimateBase(AtomicReference.class, true);
434      ClassSize.estimateBase(CellSet.class, true);
435      ClassSize.estimateBase(ReentrantReadWriteLock.class, true);
436      ClassSize.estimateBase(NonSyncTimeRangeTracker.class, true);
437      assertEquals(expected, actual);
438    }
439
440    cl = CSLMImmutableSegment.class;
441    actual = CSLMImmutableSegment.DEEP_OVERHEAD_CSLM;
442    expected = ClassSize.estimateBase(cl, false);
443    expected += 2 * ClassSize.estimateBase(AtomicLong.class, false);
444    expected += ClassSize.estimateBase(AtomicReference.class, false);
445    expected += ClassSize.estimateBase(CellSet.class, false);
446    expected += ClassSize.estimateBase(ReentrantReadWriteLock.class, false);
447    expected += ClassSize.estimateBase(NonSyncTimeRangeTracker.class, false);
448    expected += ClassSize.estimateBase(ConcurrentSkipListMap.class, false);
449    if (expected != actual) {
450      ClassSize.estimateBase(cl, true);
451      ClassSize.estimateBase(AtomicLong.class, true);
452      ClassSize.estimateBase(AtomicLong.class, true);
453      ClassSize.estimateBase(AtomicReference.class, true);
454      ClassSize.estimateBase(CellSet.class, true);
455      ClassSize.estimateBase(ReentrantReadWriteLock.class, true);
456      ClassSize.estimateBase(NonSyncTimeRangeTracker.class, true);
457      ClassSize.estimateBase(ConcurrentSkipListMap.class, true);
458      assertEquals(expected, actual);
459    }
460    cl = CellArrayImmutableSegment.class;
461    actual = CellArrayImmutableSegment.DEEP_OVERHEAD_CAM;
462    expected = ClassSize.estimateBase(cl, false);
463    expected += 2 * ClassSize.estimateBase(AtomicLong.class, false);
464    expected += ClassSize.estimateBase(AtomicReference.class, false);
465    expected += ClassSize.estimateBase(CellSet.class, false);
466    expected += ClassSize.estimateBase(ReentrantReadWriteLock.class, false);
467    expected += ClassSize.estimateBase(NonSyncTimeRangeTracker.class, false);
468    expected += ClassSize.estimateBase(CellArrayMap.class, false);
469    if (expected != actual) {
470      ClassSize.estimateBase(cl, true);
471      ClassSize.estimateBase(AtomicLong.class, true);
472      ClassSize.estimateBase(AtomicLong.class, true);
473      ClassSize.estimateBase(AtomicReference.class, true);
474      ClassSize.estimateBase(CellSet.class, true);
475      ClassSize.estimateBase(ReentrantReadWriteLock.class, true);
476      ClassSize.estimateBase(NonSyncTimeRangeTracker.class, true);
477      ClassSize.estimateBase(CellArrayMap.class, true);
478      assertEquals(expected, actual);
479    }
480
481    // Store Overhead
482    cl = HStore.class;
483    actual = HStore.FIXED_OVERHEAD;
484    expected = ClassSize.estimateBase(cl, false);
485    if (expected != actual) {
486      ClassSize.estimateBase(cl, true);
487      assertEquals(expected, actual);
488    }
489
490    // Region Overhead
491    cl = HRegion.class;
492    actual = HRegion.FIXED_OVERHEAD;
493    expected = ClassSize.estimateBase(cl, false);
494    if (expected != actual) {
495      ClassSize.estimateBase(cl, true);
496      assertEquals(expected, actual);
497    }
498
499    cl = StoreHotnessProtector.class;
500    actual = StoreHotnessProtector.FIXED_SIZE;
501    expected = ClassSize.estimateBase(cl, false);
502    if (expected != actual) {
503      ClassSize.estimateBase(cl, true);
504      assertEquals(expected, actual);
505    }
506
507    // Block cache key overhead. Only tests fixed overhead as estimating heap
508    // size of strings is hard.
509    cl = BlockCacheKey.class;
510    actual = BlockCacheKey.FIXED_OVERHEAD;
511    expected = ClassSize.estimateBase(cl, false);
512    if (expected != actual) {
513      ClassSize.estimateBase(cl, true);
514      assertEquals(expected, actual);
515    }
516
517    // Currently NOT testing Deep Overheads of many of these classes.
518    // Deep overheads cover a vast majority of stuff, but will not be 100%
519    // accurate because it's unclear when we're referencing stuff that's already
520    // accounted for. But we have satisfied our two core requirements.
521    // Sizing is quite accurate now, and our tests will throw errors if
522    // any of these classes are modified without updating overhead sizes.
523  }
524
525  @Test
526  public void testHFileBlockSize() throws IOException {
527    long expected;
528    long actual;
529
530    actual = HFileContext.FIXED_OVERHEAD;
531    expected = ClassSize.estimateBase(HFileContext.class, false);
532    assertEquals(expected, actual);
533
534    actual = HFileBlock.FIXED_OVERHEAD;
535    expected = ClassSize.estimateBase(HFileBlock.class, false);
536    assertEquals(expected, actual);
537
538    actual = ExclusiveMemHFileBlock.FIXED_OVERHEAD;
539    expected = ClassSize.estimateBase(ExclusiveMemHFileBlock.class, false);
540    assertEquals(expected, actual);
541
542    actual = SharedMemHFileBlock.FIXED_OVERHEAD;
543    expected = ClassSize.estimateBase(SharedMemHFileBlock.class, false);
544    assertEquals(expected, actual);
545  }
546
547  @Test
548  public void testMutations() {
549    Class<?> cl;
550    long expected;
551    long actual;
552
553    cl = TimeRange.class;
554    actual = ClassSize.TIMERANGE;
555    expected = ClassSize.estimateBase(cl, false);
556    if (expected != actual) {
557      ClassSize.estimateBase(cl, true);
558      assertEquals(expected, actual);
559    }
560
561    byte[] row = new byte[] { 0 };
562    cl = Put.class;
563    actual = Mutation.MUTATION_OVERHEAD + ClassSize.align(ClassSize.ARRAY);
564    expected = ClassSize.estimateBase(cl, false);
565    // The actual TreeMap is not included in the above calculation
566    expected += ClassSize.align(ClassSize.TREEMAP);
567    expected += ClassSize.align(ClassSize.INTEGER); // priority
568    if (expected != actual) {
569      ClassSize.estimateBase(cl, true);
570      assertEquals(expected, actual);
571    }
572
573    cl = Delete.class;
574    actual = Mutation.MUTATION_OVERHEAD + ClassSize.align(ClassSize.ARRAY);
575    expected = ClassSize.estimateBase(cl, false);
576    // The actual TreeMap is not included in the above calculation
577    expected += ClassSize.align(ClassSize.TREEMAP);
578    expected += ClassSize.align(ClassSize.INTEGER); // priority
579    if (expected != actual) {
580      ClassSize.estimateBase(cl, true);
581      assertEquals(expected, actual);
582    }
583  }
584
585  @Test
586  public void testReferenceSize() {
587    LOG.info("ClassSize.REFERENCE is " + ClassSize.REFERENCE);
588    // oop should be either 4 or 8
589    assertTrue(ClassSize.REFERENCE == 4 || ClassSize.REFERENCE == 8);
590  }
591
592  @Test
593  public void testObjectSize() throws IOException {
594    LOG.info("header:" + ClassSize.OBJECT);
595    LOG.info("array header:" + ClassSize.ARRAY);
596
597    if (ClassSize.is32BitJVM()) {
598      assertEquals(ClassSize.OBJECT, 8);
599    } else {
600      assertTrue(ClassSize.OBJECT == 12 || ClassSize.OBJECT == 16); // depending on CompressedOops
601    }
602    if (ClassSize.useUnsafeLayout()) {
603      assertEquals(ClassSize.ARRAY, ClassSize.OBJECT + 4);
604    } else {
605      assertEquals(ClassSize.ARRAY, ClassSize.OBJECT + 8);
606    }
607  }
608
609  private long calcFixedOverhead(List<Class<?>> classList) {
610    long overhead = 0;
611    for (Class<?> clazz : classList) {
612      overhead += ClassSize.estimateBase(clazz, false);
613    }
614    return overhead;
615  }
616
617  @Test
618  public void testAutoCalcFixedOverhead() throws InterruptedException {
619    List<Class<?>> classList = Arrays.asList(HFileContext.class, HRegion.class, BlockCacheKey.class,
620      HFileBlock.class, HStore.class, LruBlockCache.class, StoreContext.class);
621    for (int i = 0; i < 10; i++) {
622      // warm up
623      calcFixedOverhead(classList);
624    }
625    long startNs = System.nanoTime();
626    long overhead = 0;
627    for (int i = 0; i < 100; i++) {
628      overhead += calcFixedOverhead(classList);
629    }
630    long costNs = System.nanoTime() - startNs;
631    LOG.info("overhead = {}, cost {} ns", overhead, costNs);
632    // the single computation cost should be less than 5ms
633    assertThat(costNs, lessThan(TimeUnit.MILLISECONDS.toNanos(5) * classList.size() * 100));
634  }
635}