Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
src.org.github.jamm.MemoryMeter Maven / Gradle / Ivy
Go to download
Jamm provides MemoryMeter, a java agent to measure actual object memory use including JVM overhead.
package org.github.jamm;
import java.lang.instrument.Instrumentation;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.Set;
import java.util.concurrent.Callable;
public class MemoryMeter {
private static final String outerClassReference = "this\\$[0-9]+";
private static Instrumentation instrumentation;
public static void premain(String options, Instrumentation inst) {
MemoryMeter.instrumentation = inst;
}
public static void agentmain(String options, Instrumentation inst) {
MemoryMeter.instrumentation = inst;
}
public static boolean hasInstrumentation() {
return instrumentation != null;
}
public static enum Guess {
/* If instrumentation is not available, error when measuring */
NEVER,
/* If instrumentation is available, use it, otherwise guess the size using predefined specifications */
FALLBACK_SPEC,
/* If instrumentation is available, use it, otherwise guess the size using sun.misc.Unsafe */
FALLBACK_UNSAFE,
/* If instrumentation is available, use it, otherwise guess the size using sun.misc.Unsafe; if that is unavailable,
* guess using predefined specifications.*/
FALLBACK_BEST,
/* Always guess the size of measured objects using predefined specifications*/
ALWAYS_SPEC,
/* Always guess the size of measured objects using sun.misc.Unsafe */
ALWAYS_UNSAFE
}
private final Callable> trackerProvider;
private final boolean includeFullBufferSize;
private final Guess guess;
private final boolean ignoreOuterClassReference;
private final boolean ignoreKnownSingletons;
private final boolean ignoreNonStrongReferences;
private final MemoryMeterListener.Factory listenerFactory;
public MemoryMeter() {
this(new Callable>() {
public Set call() throws Exception {
// using a normal HashSet to track seen objects screws things up in two ways:
// - it can undercount objects that are "equal"
// - calling equals() can actually change object state (e.g. creating entrySet in HashMap)
return Collections.newSetFromMap(new IdentityHashMap());
}
}, true, Guess.NEVER, false, false, false, NoopMemoryMeterListener.FACTORY);
}
/**
* @param trackerProvider returns a Set with which to track seen objects and avoid cycles
* @param includeFullBufferSize
* @param guess
* @param listenerFactory the MemoryMeterListener.Factory
*/
private MemoryMeter(Callable> trackerProvider,
boolean includeFullBufferSize,
Guess guess,
boolean ignoreOuterClassReference,
boolean ignoreKnownSingletons,
boolean ignoreNonStrongReferences,
MemoryMeterListener.Factory listenerFactory) {
this.trackerProvider = trackerProvider;
this.includeFullBufferSize = includeFullBufferSize;
this.guess = guess;
this.ignoreOuterClassReference = ignoreOuterClassReference;
this.ignoreKnownSingletons = ignoreKnownSingletons;
this.ignoreNonStrongReferences = ignoreNonStrongReferences;
this.listenerFactory = listenerFactory;
}
/**
* @param trackerProvider
* @return a MemoryMeter with the given provider
*/
public MemoryMeter withTrackerProvider(Callable> trackerProvider) {
return new MemoryMeter(trackerProvider,
includeFullBufferSize,
guess,
ignoreOuterClassReference,
ignoreKnownSingletons,
ignoreNonStrongReferences,
listenerFactory);
}
/**
* @return a MemoryMeter that only counts the bytes remaining in a ByteBuffer
* in measureDeep, rather than the full size of the backing array.
* TODO: handle other types of Buffers
*/
public MemoryMeter omitSharedBufferOverhead() {
return new MemoryMeter(trackerProvider,
false,
guess,
ignoreOuterClassReference,
ignoreKnownSingletons,
ignoreNonStrongReferences,
listenerFactory);
}
/**
* @return a MemoryMeter that permits guessing the size of objects if instrumentation isn't enabled
*/
public MemoryMeter withGuessing(Guess guess) {
return new MemoryMeter(trackerProvider,
includeFullBufferSize,
guess,
ignoreOuterClassReference,
ignoreKnownSingletons,
ignoreNonStrongReferences,
listenerFactory);
}
/**
* @return a MemoryMeter that ignores the size of an outer class reference
*/
public MemoryMeter ignoreOuterClassReference() {
return new MemoryMeter(trackerProvider,
includeFullBufferSize,
guess,
true,
ignoreKnownSingletons,
ignoreNonStrongReferences,
listenerFactory);
}
/**
* return a MemoryMeter that ignores space occupied by known singletons such as Class objects and Enums
*/
public MemoryMeter ignoreKnownSingletons() {
return new MemoryMeter(trackerProvider,
includeFullBufferSize,
guess,
ignoreOuterClassReference,
true,
ignoreNonStrongReferences,
listenerFactory);
}
/**
* return a MemoryMeter that ignores space occupied by known singletons such as Class objects and Enums
*/
public MemoryMeter ignoreNonStrongReferences() {
return new MemoryMeter(trackerProvider,
includeFullBufferSize,
guess,
ignoreOuterClassReference,
ignoreKnownSingletons,
true,
listenerFactory);
}
/**
* Makes this MemoryMeter
prints the classes tree to System.out
when measuring
*/
public MemoryMeter enableDebug() {
return enableDebug(Integer.MAX_VALUE);
}
/**
* Makes this MemoryMeter
prints the classes tree to System.out
up to the specified depth
* when measuring
* @param depth the maximum depth for which the class tree must be printed
*/
public MemoryMeter enableDebug(int depth) {
if (depth <= 0)
throw new IllegalArgumentException(String.format("the depth must be greater than zero (was %s).", depth));
return new MemoryMeter(trackerProvider,
includeFullBufferSize,
guess,
ignoreOuterClassReference,
ignoreKnownSingletons,
ignoreNonStrongReferences,
new TreePrinter.Factory(depth));
}
/**
* @return the shallow memory usage of @param object
* @throws NullPointerException if object is null
*/
public long measure(Object object) {
switch (guess) {
case ALWAYS_UNSAFE:
return MemoryLayoutSpecification.sizeOfWithUnsafe(object);
case ALWAYS_SPEC:
return MemoryLayoutSpecification.sizeOf(object);
default:
if (instrumentation == null) {
switch (guess) {
case NEVER:
throw new IllegalStateException("Instrumentation is not set; Jamm must be set as -javaagent");
case FALLBACK_UNSAFE:
if (!MemoryLayoutSpecification.hasUnsafe())
throw new IllegalStateException("Instrumentation is not set and sun.misc.Unsafe could not be obtained; Jamm must be set as -javaagent, or the SecurityManager must permit access to sun.misc.Unsafe");
//$FALL-THROUGH$
case FALLBACK_BEST:
if (MemoryLayoutSpecification.hasUnsafe())
return MemoryLayoutSpecification.sizeOfWithUnsafe(object);
//$FALL-THROUGH$
case FALLBACK_SPEC:
return MemoryLayoutSpecification.sizeOf(object);
}
}
return instrumentation.getObjectSize(object);
}
}
/**
* @return the memory usage of @param object including referenced objects
* @throws NullPointerException if object is null
*/
public long measureDeep(Object object) {
if (object == null) {
throw new NullPointerException(); // match getObjectSize behavior
}
if (ignoreClass(object.getClass()))
return 0;
Set tracker;
try {
tracker = trackerProvider.call();
}
catch (Exception e) {
throw new RuntimeException(e);
}
MemoryMeterListener listener = listenerFactory.newInstance();
tracker.add(object);
listener.started(object);
// track stack manually so we can handle deeper hierarchies than recursion
Deque stack = new ArrayDeque();
stack.push(object);
long total = 0;
while (!stack.isEmpty()) {
Object current = stack.pop();
assert current != null;
long size = measure(current);
listener.objectMeasured(current, size);
total += size;
if (current instanceof Object[]) {
addArrayChildren((Object[]) current, stack, tracker, listener);
} else if (current instanceof ByteBuffer && !includeFullBufferSize) {
total += ((ByteBuffer) current).remaining();
} else {
Object referent = (ignoreNonStrongReferences && (current instanceof Reference)) ? ((Reference>)current).get() : null;
addFieldChildren(current, stack, tracker, referent, listener);
}
}
listener.done(total);
return total;
}
/**
* @return the number of child objects referenced by @param object
* @throws NullPointerException if object is null
*/
public long countChildren(Object object) {
if (object == null) {
throw new NullPointerException();
}
MemoryMeterListener listener = listenerFactory.newInstance();
Set tracker = Collections.newSetFromMap(new IdentityHashMap());
tracker.add(object);
listener.started(object);
Deque stack = new ArrayDeque();
stack.push(object);
long total = 0;
while (!stack.isEmpty()) {
Object current = stack.pop();
assert current != null;
total++;
listener.objectCounted(current);
if (current instanceof Object[]) {
addArrayChildren((Object[]) current, stack, tracker, listener);
} else {
Object referent = (ignoreNonStrongReferences && (current instanceof Reference)) ? ((Reference>)current).get() : null;
addFieldChildren(current, stack, tracker, referent, listener);
}
}
listener.done(total);
return total;
}
private void addFieldChildren(Object current, Deque stack, Set tracker, Object ignorableChild, MemoryMeterListener listener) {
Class> cls = current.getClass();
while (!skipClass(cls)) {
for (Field field : cls.getDeclaredFields()) {
if (field.getType().isPrimitive()
|| Modifier.isStatic(field.getModifiers())
|| field.isAnnotationPresent(Unmetered.class)) {
continue;
}
if (ignoreOuterClassReference && field.getName().matches(outerClassReference)) {
continue;
}
if (ignoreClass(field.getType())) {
continue;
}
field.setAccessible(true);
Object child;
try {
child = field.get(current);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
if (child != ignorableChild) {
if (child != null && !tracker.contains(child)) {
stack.push(child);
tracker.add(child);
listener.fieldAdded(current, field.getName(), child);
}
}
}
cls = cls.getSuperclass();
}
}
private static final Class clsJLRModule;
private static final Class clsJLRAccessibleObject;
private static final Class clsSRAAnnotationInvocationHandler;
private static final Class clsSRAAnnotationType;
private static final Class clsJIRUnsafeFieldAccessorImpl;
private static final Class clsJIRDelegatingMethodAccessorImpl;
static
{
clsJLRModule = maybeGetClass("java.lang.reflect.Module");
clsJLRAccessibleObject = maybeGetClass("java.lang.reflect.AccessibleObject");
clsSRAAnnotationInvocationHandler = maybeGetClass("sun.reflect.annotation.AnnotationInvocationHandler");
clsSRAAnnotationType = maybeGetClass("sun.reflect.annotation.AnnotationType");
clsJIRUnsafeFieldAccessorImpl = maybeGetClass("jdk.internal.reflect.UnsafeFieldAccessorImpl");
clsJIRDelegatingMethodAccessorImpl = maybeGetClass("jdk.internal.reflect.DelegatingMethodAccessorImpl");
}
private static Class> maybeGetClass(String name)
{
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
return null;
}
}
private boolean skipClass(Class> cls) {
return cls == null
|| cls == clsJLRModule || cls == clsJLRAccessibleObject
|| cls == clsSRAAnnotationInvocationHandler || cls == clsSRAAnnotationType
|| cls == clsJIRUnsafeFieldAccessorImpl || cls == clsJIRDelegatingMethodAccessorImpl;
}
private boolean ignoreClass(Class> cls) {
return (ignoreKnownSingletons && (cls.equals(Class.class) || Enum.class.isAssignableFrom(cls)))
|| isAnnotationPresent(cls);
}
private boolean isAnnotationPresent(Class> cls) {
if (cls == null)
return false;
if (cls.isAnnotationPresent(Unmetered.class))
return true;
Class>[] interfaces = cls.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (isAnnotationPresent(cls.getInterfaces()[i]))
return true;
}
return isAnnotationPresent(cls.getSuperclass());
}
private void addArrayChildren(Object[] current, Deque stack, Set tracker, MemoryMeterListener listener) {
for (int i = 0; i < current.length; i++) {
Object child = current[i];
if (child != null && !tracker.contains(child)) {
Class> childCls = child.getClass();
if (ignoreClass(childCls)) {
continue;
}
stack.push(child);
tracker.add(child);
listener.fieldAdded(current, Integer.toString(i) , child);
}
}
}
}