package com.github.ompc.greys.core.util; import java.lang.instrument.Instrumentation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayDeque; import java.util.IdentityHashMap; /** * 计算一个对象的大小 * Created by vlinux on 15/12/10. */ public class SizeOf { public enum HeapType { /** * 浅引用大小 */ SHALLOW, /** * 深引用大小 */ RETAINED } private final Instrumentation inst; private final long size; public SizeOf(final Instrumentation inst, final Object object, final HeapType heapType) { this.inst = inst; if (null == inst || null == object) { this.size = 0L; return; } switch (heapType) { case SHALLOW: { this.size = getShallowSize(object); break; } case RETAINED: { this.size = getRetainedSize(object); break; } default: { this.size = 0; } } } /** * 计算浅引用 * * @param target 目标对象 * @return 目标对象在JVM-HEAP中的浅引用对象大小 */ private long getShallowSize(final Object target) { return inst.getObjectSize(target); } /** * 计算深引用 * * @param target 目标对象 * @return 目标对象在JVM-HEAP中的深引用对象大小 */ private long getRetainedSize(final Object target) { long result = getShallowSize(target); final IdentityHashMap<Object, Void> references = new IdentityHashMap<Object, Void>(); references.put(target, null); final ArrayDeque<Object> unprocessed = new ArrayDeque<Object>(); unprocessed.addFirst(target); do { Object node = unprocessed.removeFirst(); Class<?> nodeClass = node.getClass(); if (nodeClass.isArray()) { if (node.getClass().getComponentType().isPrimitive()) { continue; } int length = Array.getLength(node); for (int i = 0; i < length; ++i) { Object elem = Array.get(node, i); if (elem == null) { continue; } if (references.containsKey(elem)) { continue; } unprocessed.addFirst(elem); references.put(elem, null); result += getShallowSize(elem); } continue; } while (nodeClass != null) { // traverse up until we hit Object for (Field field : nodeClass.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers())) { continue; } field.setAccessible(true); try { Class<?> type = field.getType(); // primitive types if (type.isPrimitive()) { continue; } // reference types Object value = field.get(node); if (value == null) { continue; } if (references.containsKey(value)) { continue; } if (isSharedFlyweight(value)) { continue; } unprocessed.addFirst(value); references.put(value, null); result += getShallowSize(value); } catch (IllegalArgumentException e) { return -1L; } catch (IllegalAccessException e) { return -2L; } } nodeClass = nodeClass.getSuperclass(); } } while (!unprocessed.isEmpty()); return result; } /** * Returns true if this is a well-known shared flyweight. * For example, interned Strings, Booleans and Number objects. * <p/> * thanks to Dr. Heinz Kabutz * see http://www.javaspecialists.co.za/archive/Issue142.html */ private static boolean isSharedFlyweight(Object obj) { // optimization - all of our flyweights are Comparable if (obj instanceof Comparable) { if (obj instanceof Enum) { return true; } else if (obj instanceof String) { return (obj == ((String) obj).intern()); } else if (obj instanceof Boolean) { return (obj == Boolean.TRUE || obj == Boolean.FALSE); } else if (obj instanceof Integer) { return (obj == Integer.valueOf((Integer) obj)); } else if (obj instanceof Short) { return (obj == Short.valueOf((Short) obj)); } else if (obj instanceof Byte) { return (obj == Byte.valueOf((Byte) obj)); } else if (obj instanceof Long) { return (obj == Long.valueOf((Long) obj)); } else if (obj instanceof Character) { return (obj == Character.valueOf((Character) obj)); } } return false; } /** * 获取对象大小 * * @return 对象大小 */ public long getSize() { return size; } @Override public String toString() { if (size < 1024) return size + " B"; int z = (63 - Long.numberOfLeadingZeros(size)) / 10; return String.format("%.1f %sB", (double)size / (1L << (z*10)), " KMGTPE".charAt(z)); } }