// ================================================================================================= // Copyright 2011 Twitter, Inc. // ------------------------------------------------------------------------------------------------- // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this work except in compliance with the License. // You may obtain a copy of the License in the LICENSE file, or at: // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ================================================================================================= package com.twitter.common.objectsize; import org.junit.Before; import org.junit.Test; import com.twitter.common.objectsize.ObjectSizeCalculator.MemoryLayoutSpecification; import static org.junit.Assert.assertEquals; public class ObjectSizeCalculatorTest { private int A; private int O; private int R; private int S; private ObjectSizeCalculator objectSizeCalculator; @Before public void setUp() { MemoryLayoutSpecification memoryLayoutSpecification = new MemoryLayoutSpecification() { @Override public int getArrayHeaderSize() { return 16; } @Override public int getObjectHeaderSize() { return 12; } @Override public int getObjectPadding() { return 8; } @Override public int getReferenceSize() { return 4; } @Override public int getSuperclassFieldPadding() { return 4; } }; A = memoryLayoutSpecification.getArrayHeaderSize(); O = memoryLayoutSpecification.getObjectHeaderSize(); R = memoryLayoutSpecification.getReferenceSize(); S = memoryLayoutSpecification.getSuperclassFieldPadding(); objectSizeCalculator = new ObjectSizeCalculator(memoryLayoutSpecification); } @Test public void testRounding() { assertEquals(0, roundTo(0, 8)); assertEquals(8, roundTo(1, 8)); assertEquals(8, roundTo(7, 8)); assertEquals(8, roundTo(8, 8)); assertEquals(16, roundTo(9, 8)); assertEquals(16, roundTo(15, 8)); assertEquals(16, roundTo(16, 8)); assertEquals(24, roundTo(17, 8)); } @Test public void testObjectSize() { assertSizeIs(O, new Object()); } static class ObjectWithFields { int length; int offset; int hashcode; char[] data = {}; } @Test public void testObjectWithFields() { assertSizeIs(O + 3 * 4 + R + A, new ObjectWithFields()); } public static class Class1 { private boolean b1; } @Test public void testOneBooleanSize() { assertSizeIs(O + 1, new Class1()); } public static class Class2 extends Class1 { private int i1; } @Test public void testSimpleSubclassSize() { assertSizeIs(O + roundTo(1, S) + 4, new Class2()); } @Test public void testZeroLengthArray() { assertSizeIs(A, new byte[0]); assertSizeIs(A, new int[0]); assertSizeIs(A, new long[0]); assertSizeIs(A, new Object[0]); } @Test public void testByteArrays() { assertSizeIs(A + 1, new byte[1]); assertSizeIs(A + 8, new byte[8]); assertSizeIs(A + 9, new byte[9]); } @Test public void testCharArrays() { assertSizeIs(A + 2 * 1, new char[1]); assertSizeIs(A + 2 * 4, new char[4]); assertSizeIs(A + 2 * 5, new char[5]); } @Test public void testIntArrays() { assertSizeIs(A + 4 * 1, new int[1]); assertSizeIs(A + 4 * 2, new int[2]); assertSizeIs(A + 4 * 3, new int[3]); } @Test public void testLongArrays() { assertSizeIs(A + 8 * 1, new long[1]); assertSizeIs(A + 8 * 2, new long[2]); assertSizeIs(A + 8 * 3, new long[3]); } @Test public void testObjectArrays() { assertSizeIs(A + R * 1, new Object[1]); assertSizeIs(A + R * 2, new Object[2]); assertSizeIs(A + R * 3, new Object[3]); assertSizeIs(A + R * 1, new String[1]); assertSizeIs(A + R * 2, new String[2]); assertSizeIs(A + R * 3, new String[3]); } public static class Circular { Circular c; } @Test public void testCircular() { Circular c1 = new Circular(); long size = objectSizeCalculator.calculateObjectSize(c1); c1.c = c1; assertEquals(size, objectSizeCalculator.calculateObjectSize(c1)); } static class ComplexObject<T> { static class Node<T> { final T value; Node<T> previous; Node<T> next; Node(T value) { this.value = value; } } private Node<T> first; private Node<T> last; void add(T item) { Node<T> node = new Node<T>(item); if (first == null) { first = node; } else { last.next = node; node.previous = last; } last = node; } } @Test public void testComplexObject() { ComplexObject<Object> l = new ComplexObject<Object>(); l.add(new Object()); l.add(new Object()); l.add(new Object()); long expectedSize = 0; expectedSize += roundTo(O + 2 * R, 8); // The complex object itself plus first and last refs. expectedSize += roundTo(O + 3 * R, 8) * 3; // 3 Nodes - each with 3 object references. expectedSize += roundTo(O, 8) * 3; // 3 vanilla objects contained in the node values. assertSizeIs(expectedSize, l); } private void assertSizeIs(long size, Object o) { assertEquals(roundTo(size, 8), objectSizeCalculator.calculateObjectSize(o)); } private static long roundTo(long x, int multiple) { return ObjectSizeCalculator.roundTo(x, multiple); } }