/* * Copyright 2014-2015 JKOOL, LLC. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License 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.jkoolcloud.tnt4j.utils; import java.lang.instrument.Instrumentation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.IdentityHashMap; import java.util.Map; /** * This class computes estimated memory footprint of a specific object. The implementation provides shallow size as well * as deep size which includes all reachable objects. This class must be included in the "javaagent:" command line * option to be able to compute object sizes. * * @version $Revision: 2 $ */ public class SizeOf { /** * Instance of java.lang.instrument.Instrument injected by the Java VM * * @see #premain(String options, Instrumentation inst) */ private static Instrumentation inst; private static boolean SKIP_STATIC_FIELD = false; private static boolean SKIP_FINAL_FIELD = false; private static boolean SKIP_FLYWEIGHT_FIELD = false; private SizeOf() { } /** * Callback method used by the Java VM to inject the java.lang.instrument.Instrument instance * * @param options agent arguments * @param inst instrumenter */ public static void premain(String options, Instrumentation inst) { SizeOf.inst = inst; } /** * Calls java.lang.instrument.Instrument.getObjectSize(object). * * @param object * the object to size * @return an implementation-specific approximation of the amount of storage consumed by the specified object. * */ public static long sizeOf(Object object) { if ((inst == null) || (SKIP_FLYWEIGHT_FIELD && isSharedFlyweight(object))) return 0; return inst.getObjectSize(object); } /** * Compute an implementation-specific approximation of the amount of storage consumed by objectToSize and by all the * objects reachable from it * * @param objectToSize object whose size is to be approximated * @return an implementation-specific approximation of the amount of storage consumed by objectToSize and by all the * objects reachable from it */ public static long deepSizeOf(Object objectToSize) { Map<Object, Long> doneObj = new IdentityHashMap<Object, Long>(); return deepSizeOf(objectToSize, doneObj, null, 0); } /** * Compute an implementation-specific approximation of the amount of storage consumed by objectToSize and by all the * objects reachable from it * * @param objectToSize object whose size is to be approximated * @param doneFields set of fields already computed (along with their sizes) * @return an implementation-specific approximation of the amount of storage consumed by objectToSize and by all the * objects reachable from it */ public static long deepSizeOf(Object objectToSize, Map<Field, Long> doneFields) { Map<Object, Long> doneObj = new IdentityHashMap<Object, Long>(); return deepSizeOf(objectToSize, doneObj, doneFields, 0); } private static long deepSizeOf(Object o, Map<Object, Long> doneObj, Map<Field, Long> doneFields, int depth) { if (o == null || (inst == null)) { return 0; } long size; if (doneObj.containsKey(o)) { return 0; } size = sizeOf(o); doneObj.put(o, size); // record initial size Class<?> clazz = o.getClass(); if (clazz.isArray()) { int length = Array.getLength(o); for (int i = 0; i < length; i++) { size += deepSizeOf(Array.get(o, i), doneObj, doneFields, depth + 1); } } else { do { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Object obj; try { obj = field.get(o); } catch (Throwable e) { throw new RuntimeException(e); } if (isComputable(field)) { long fSize = deepSizeOf(obj, doneObj, doneFields, depth + 1); size += fSize; if (doneFields != null) doneFields.put(field, fSize); } } clazz = clazz.getSuperclass(); } while (clazz != null); } doneObj.put(o, size); // record final size return size; } /** * Determines if the field is computable based on flags set for primitive, static and final fields. * * @param field to test * @return true if the field must be computed */ private static boolean isComputable(Field field) { int modificatori = field.getModifiers(); if (field.getType().isPrimitive()) return false; else if (SKIP_STATIC_FIELD && Modifier.isStatic(modificatori)) return false; else if (SKIP_FINAL_FIELD && Modifier.isFinal(modificatori)) return false; else return true; } /** * Returns true if this is a well-known shared flyweight. For example, interned Strings, Booleans and Number * objects. * */ 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; } /** * If true deepSizeOf() doesn't compute the final fields of an object. Default value is false. * * @param skip_final_field flag to skip final fields */ public static void skipFinal(boolean skip_final_field) { SKIP_FINAL_FIELD = skip_final_field; } /** * If true deepSizeOf() doesn't compute the static fields of an object. Default value is false. * * @param skip_static_field flag to skip static fields */ public static void skipStatic(boolean skip_static_field) { SKIP_STATIC_FIELD = skip_static_field; } /** * If true flyweight objects has a size of 0. Default value is false. * * @param skip flag to skip flyweight objects */ public static void skipFlyweight(boolean skip) { SKIP_FLYWEIGHT_FIELD = skip; } }