# HG changeset patch # User Thomas Wuerthinger # Date 1392653201 -3600 # Node ID be0d961e3a88e3fd857b828f6879902c3b3bbdcd # Parent d68f5d0c97f0ba38779027353bc30dfc25d576d8 New methods for querying memory usage of individual objects and object graphs in Graal API (MetaAccessProvider#getMemorySize, MetaUtil#getMemorySizeRecursive). diff -r d68f5d0c97f0 -r be0d961e3a88 CHANGELOG.md --- a/CHANGELOG.md Mon Feb 17 13:48:41 2014 +0100 +++ b/CHANGELOG.md Mon Feb 17 17:06:41 2014 +0100 @@ -2,6 +2,7 @@ ## `tip` ### Graal +* New methods for querying memory usage of individual objects and object graphs in Graal API (MetaAccessProvider#getMemorySize, MetaUtil#getMemorySizeRecursive). * ... ### Truffle diff -r d68f5d0c97f0 -r be0d961e3a88 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Kind.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Kind.java Mon Feb 17 13:48:41 2014 +0100 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Kind.java Mon Feb 17 17:06:41 2014 +0100 @@ -383,6 +383,19 @@ } /** + * Number of bytes that are necessary to represent a value of this kind. + * + * @return the number of bytes + */ + public int getByteCount() { + if (this == Boolean) { + return 1; + } else { + return getBitCount() >> 3; + } + } + + /** * Number of bits that are necessary to represent a value of this kind. * * @return the number of bits diff -r d68f5d0c97f0 -r be0d961e3a88 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java Mon Feb 17 13:48:41 2014 +0100 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java Mon Feb 17 17:06:41 2014 +0100 @@ -60,6 +60,14 @@ ResolvedJavaType lookupJavaType(Constant constant); /** + * Returns the number of bytes occupied by this constant value or constant object. + * + * @param constant the constant whose bytes should be measured + * @return the number of bytes occupied by this constant + */ + long getMemorySize(Constant constant); + + /** * Parses a method * descriptor into a {@link Signature}. The behavior of this method is undefined if the diff -r d68f5d0c97f0 -r be0d961e3a88 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java Mon Feb 17 13:48:41 2014 +0100 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java Mon Feb 17 17:06:41 2014 +0100 @@ -24,6 +24,7 @@ import static java.lang.reflect.Modifier.*; +import java.io.*; import java.lang.annotation.*; import java.lang.reflect.*; import java.util.*; @@ -36,6 +37,104 @@ */ public class MetaUtil { + private static class ClassInfo { + public long totalSize; + public long instanceCount; + + @Override + public String toString() { + return "totalSize=" + totalSize + ", instanceCount=" + instanceCount; + } + } + + /** + * Returns the number of bytes occupied by this constant value or constant object and + * recursively all values reachable from this value. + * + * @param constant the constant whose bytes should be measured + * @param printTopN print total size and instance count of the top n classes is desired + * @return the number of bytes occupied by this constant + */ + public static long getMemorySizeRecursive(MetaAccessProvider access, Constant constant, PrintStream out, int printTopN) { + IdentityHashMap marked = new IdentityHashMap<>(); + Stack stack = new Stack<>(); + if (constant.getKind() == Kind.Object && constant.isNonNull()) { + marked.put(constant.asObject(), Boolean.TRUE); + } + final HashMap histogram = new HashMap<>(); + stack.push(constant); + long sum = 0; + while (!stack.isEmpty()) { + Constant c = stack.pop(); + long memorySize = access.getMemorySize(constant); + sum += memorySize; + if (c.getKind() == Kind.Object && c.isNonNull()) { + Class clazz = c.asObject().getClass(); + if (!histogram.containsKey(clazz)) { + histogram.put(clazz, new ClassInfo()); + } + ClassInfo info = histogram.get(clazz); + info.instanceCount++; + info.totalSize += memorySize; + ResolvedJavaType type = access.lookupJavaType(c); + if (type.isArray()) { + if (!type.getComponentType().isPrimitive()) { + Object[] array = (Object[]) c.asObject(); + for (Object value : array) { + Constant forObject = Constant.forObject(value); + pushConstant(marked, stack, forObject); + } + } + } else { + ResolvedJavaField[] instanceFields = type.getInstanceFields(true); + for (ResolvedJavaField f : instanceFields) { + if (f.getKind() == Kind.Object) { + Constant value = f.readValue(c); + pushConstant(marked, stack, value); + } + } + } + } + } + ArrayList clazzes = new ArrayList<>(); + clazzes.addAll(histogram.keySet()); + Collections.sort(clazzes, new Comparator() { + + @Override + public int compare(Class o1, Class o2) { + long l1 = histogram.get(o1).totalSize; + long l2 = histogram.get(o2).totalSize; + if (l1 > l2) { + return -1; + } else if (l1 == l2) { + return 0; + } else { + return 1; + } + } + }); + + int z = 0; + for (Class c : clazzes) { + if (z > printTopN) { + break; + } + out.println("Class " + c + ", " + histogram.get(c)); + ++z; + } + + return sum; + } + + private static void pushConstant(IdentityHashMap marked, Stack stack, Constant value) { + if (value.isNonNull()) { + if (!marked.containsKey(value.asObject())) { + marked.put(value.asObject(), Boolean.TRUE); + stack.push(value); + } + } + } + /** * Returns true if the specified typed is exactly the type {@link java.lang.Object}. */ diff -r d68f5d0c97f0 -r be0d961e3a88 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMetaAccessProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMetaAccessProvider.java Mon Feb 17 13:48:41 2014 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMetaAccessProvider.java Mon Feb 17 17:06:41 2014 +0100 @@ -26,9 +26,11 @@ import java.lang.reflect.*; +import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.graph.*; import com.oracle.graal.hotspot.*; +import com.oracle.graal.hotspot.replacements.*; /** * HotSpot implementation of {@link MetaAccessProvider}. @@ -284,4 +286,30 @@ } throw GraalInternalError.shouldNotReachHere(Integer.toHexString(reason)); } + + @Override + public long getMemorySize(Constant constant) { + if (constant.getKind() == Kind.Object) { + HotSpotResolvedObjectType lookupJavaType = (HotSpotResolvedObjectType) this.lookupJavaType(constant); + + if (lookupJavaType == null) { + return 0; + } else { + if (lookupJavaType.isArray()) { + // TODO(tw): Add compressed pointer support. + int length = Array.getLength(constant.asObject()); + ResolvedJavaType elementType = lookupJavaType.getComponentType(); + Kind elementKind = elementType.getKind(); + final int headerSize = HotSpotGraalRuntime.getArrayBaseOffset(elementKind); + int sizeOfElement = HotSpotGraalRuntime.runtime().getTarget().arch.getSizeInBytes(elementKind); + int alignment = HotSpotGraalRuntime.runtime().getTarget().wordSize; + int log2ElementSize = CodeUtil.log2(sizeOfElement); + return NewObjectSnippets.computeArrayAllocationSize(length, alignment, headerSize, log2ElementSize); + } + return lookupJavaType.instanceSize(); + } + } else { + return constant.getKind().getByteCount(); + } + } }