/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2015 Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.broad.igv.util;
import com.google.common.primitives.Primitives;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Do not add any more dependencies to this class than necessary.
* Packaged into JavaAgent.jar, for memory profiling
* @author jacob
* @date 2013-Oct-04
*/
public class JavaAgent {
/**
* Size, in BITS, of each primitive
*/
private static Map<Class, Long> primitiveMemMap = new HashMap<Class, Long>(9);
static{
primitiveMemMap.put(Byte.TYPE, 8l);
primitiveMemMap.put(Short.TYPE, 16l);
primitiveMemMap.put(Integer.TYPE, 32l);
primitiveMemMap.put(Long.TYPE, 64l);
primitiveMemMap.put(Float.TYPE, 32l);
primitiveMemMap.put(Double.TYPE, 64l);
//May be an overestimate
primitiveMemMap.put(Boolean.TYPE, 1l);
primitiveMemMap.put(Character.TYPE, 16l);
primitiveMemMap.put(Void.TYPE, 0l);
assert primitiveMemMap.size() == Primitives.allPrimitiveTypes().size();
}
private static Instrumentation instrumentation;
public static void premain(String args, Instrumentation inst) {
instrumentation = inst;
}
/**
* Must invoke with instrumentation on command line
* Note: Not recursive
*
* @param o
* @return
*/
public static long getObjectSize(Object o) {
if (instrumentation == null) {
throw new IllegalStateException("No instrumentation available. Need to launch width -javaagent:path/to/RuntimeUtils.jar");
}
//Maybe not right
if(o == null) return 4;
return instrumentation.getObjectSize(o);
}
public static long getObjectSizeRecursive(Object o, Set<Object> completedObjs){
try{
if(o != null && completedObjs.contains(o)) return 0;
}catch(Exception e){
System.out.println("Error on " + o.getClass() + " skipping");
return 0;
}
long fullSize = getObjectSize(o);
if(o == null) return fullSize;
completedObjs.add(o);
//May be off a bit for wrapper classes,
//but we let that go for now
if(isPrimitiveOrWrapper(o)){
return fullSize;
}
if(o.getClass().isArray()){
for(int ii=0; ii < Array.getLength(o); ii++){
Object el = Array.get(o, ii);
fullSize += getObjectSizeRecursive(el, completedObjs);
}
}
Set<Field> fields = new HashSet<Field>();
getAllFields(o.getClass(), fields);
for(Field field: fields){
if(Modifier.isStatic(field.getModifiers())){
continue;
}
field.setAccessible(true);
try {
Object fieldValue = field.get(o);
//Field.get boxes primitives as wrapper classes,
//which would artificially inflate the total
Class fieldType = field.getType();
if(fieldType.isPrimitive()){
fullSize += primitiveMemMap.get(fieldType);
}else{
fullSize += getObjectSizeRecursive(fieldValue, completedObjs);
}
completedObjs.add(fieldValue);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch(NullPointerException e){
System.out.println("Error on " + o.getClass() + " skipping");
}
}
return fullSize;
}
static boolean isPrimitiveOrWrapper(Object o){
return o.getClass().isPrimitive() || Primitives.isWrapperType(o.getClass());
}
/**
* Get all fields an object contains, including public, private, protected,
* and inherited
* @param clazz
* @param fields
*/
static void getAllFields(Class clazz, Set<Field> fields){
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
Class supClass = clazz.getSuperclass();
if(supClass != null){
getAllFields(supClass, fields);
}
}
}