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