summaryrefslogtreecommitdiffstats
path: root/vespajlib/src/main/java/com/yahoo/cache/SizeCalculator.java
diff options
context:
space:
mode:
Diffstat (limited to 'vespajlib/src/main/java/com/yahoo/cache/SizeCalculator.java')
-rw-r--r--vespajlib/src/main/java/com/yahoo/cache/SizeCalculator.java175
1 files changed, 175 insertions, 0 deletions
diff --git a/vespajlib/src/main/java/com/yahoo/cache/SizeCalculator.java b/vespajlib/src/main/java/com/yahoo/cache/SizeCalculator.java
new file mode 100644
index 00000000000..677a3fb07e6
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/cache/SizeCalculator.java
@@ -0,0 +1,175 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.cache;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Size calculator for objects.
+ * Thread safe.
+ * @author vegardh
+ * @see <a href="http://www.javaspecialists.co.za/archive/Issue078.html">MemoryCounter by Dr H M Kabutz</a>
+ */
+public class SizeCalculator {
+
+ private static class ObjectSet {
+ private final Map<Object, Object> map = new IdentityHashMap<>();
+
+ public boolean had(Object obj) {
+ if (map.containsKey(obj)) {
+ return true;
+ }
+ map.put(obj, null);
+ return false;
+ }
+ }
+
+ private int getPointerSize() {
+ return 4;
+ }
+
+ private int getClassSize() {
+ return 8;
+ }
+
+ private int getArraySize() {
+ return 16;
+ }
+
+ @SuppressWarnings("serial")
+ private final IdentityHashMap<Class<?>, Integer> primitiveSizes = new IdentityHashMap<Class<?>, Integer>() {
+ {
+ put(boolean.class, 1);
+ put(byte.class, 1);
+ put(char.class, 2);
+ put(short.class, 2);
+ put(int.class, 4);
+ put(float.class, 4);
+ put(double.class, 8);
+ put(long.class, 8);
+ }
+ };
+
+ // Only called on un-visited objects and only with array.
+ private long sizeOfArray(Object a, ObjectSet visitedObjects) {
+ long sum = getArraySize();
+ int length = Array.getLength(a);
+ if (length == 0) {
+ return sum;
+ }
+ Class<?> elementClass = a.getClass().getComponentType();
+ if (elementClass.isPrimitive()) {
+ sum += length * (primitiveSizes.get(elementClass));
+ return sum;
+ } else {
+ for (int i = 0; i < length; i++) {
+ Object val = Array.get(a, i);
+ sum += getPointerSize();
+ sum += sizeOfObject(val, visitedObjects);
+ }
+ return sum;
+ }
+ }
+
+ private long getSumOfFields(Class<?> clas, Object obj,
+ ObjectSet visitedObjects) {
+ long sum = 0;
+ Field[] fields = clas.getDeclaredFields();
+ for (Field field : fields) {
+ if (!Modifier.isStatic(field.getModifiers())) {
+ if (field.getType().isPrimitive()) {
+ sum += primitiveSizes.get(field.getType());
+ } else {
+ sum += getPointerSize();
+ field.setAccessible(true);
+ try {
+ sum += sizeOfObject(field.get(obj), visitedObjects);
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ return sum;
+ }
+
+ // Skip literal strings
+ private boolean isIntern(Object obj) {
+ if (obj instanceof String) {
+ if (obj == ((String) obj).intern()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Only called on non-visited non-arrays.
+ private long sizeOfNonArray(Class<?> clas, Object obj,
+ ObjectSet visitedObjects) {
+ if (isIntern(obj)) {
+ return 0;
+ }
+ long sum = getClassSize();
+ while (clas != null) {
+ sum += getSumOfFields(clas, obj, visitedObjects);
+ clas = clas.getSuperclass();
+ }
+ return sum;
+ }
+
+ private long sizeOfObject(Object obj, ObjectSet visitedObjects) {
+ if (obj == null) {
+ return 0;
+ }
+ if (visitedObjects.had(obj)) {
+ return 0;
+ }
+ Class<?> clas = obj.getClass();
+ if (clas.isArray()) {
+ return sizeOfArray(obj, visitedObjects);
+ }
+ return sizeOfNonArray(clas, obj, visitedObjects);
+ }
+
+ /**
+ * Returns the heap size of an object/array
+ *
+ * @return Number of bytes for object, approximately
+ */
+ public long sizeOf(Object value) {
+ ObjectSet visitedObjects = new ObjectSet();
+ return sizeOfObject(value, visitedObjects);
+ }
+
+ /**
+ * Returns the heap size of two objects/arrays, common objects counted only
+ * once
+ *
+ * @return Number of bytes for objects, approximately
+ */
+ public long sizeOf(Object value1, Object value2) {
+ ObjectSet visitedObjects = new ObjectSet();
+ return sizeOfObject(value1, visitedObjects)
+ + sizeOfObject(value2, visitedObjects);
+ }
+
+ /**
+ * The approximate size in bytes for a list of objects, viewed as a closure,
+ * ie. common objects are counted only once.
+ *
+ * @return total number of bytes
+ */
+ public long sizeOf(List<?> objects) {
+ ObjectSet visitedObjects = new ObjectSet();
+ long sum = 0;
+ for (Object o : objects) {
+ sum += sizeOfObject(o, visitedObjects);
+ }
+ return sum;
+ }
+
+}