changeset 13966:be0d961e3a88

New methods for querying memory usage of individual objects and object graphs in Graal API (MetaAccessProvider#getMemorySize, MetaUtil#getMemorySizeRecursive).
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Mon, 17 Feb 2014 17:06:41 +0100
parents d68f5d0c97f0
children 4cd7c6629841
files CHANGELOG.md graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Kind.java graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMetaAccessProvider.java
diffstat 5 files changed, 149 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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
--- 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 <a
      * href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3">method
      * descriptor</a> into a {@link Signature}. The behavior of this method is undefined if the
--- 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<Object, Boolean> marked = new IdentityHashMap<>();
+        Stack<Constant> stack = new Stack<>();
+        if (constant.getKind() == Kind.Object && constant.isNonNull()) {
+            marked.put(constant.asObject(), Boolean.TRUE);
+        }
+        final HashMap<Class, ClassInfo> 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<Class> clazzes = new ArrayList<>();
+        clazzes.addAll(histogram.keySet());
+        Collections.sort(clazzes, new Comparator<Class>() {
+
+            @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<Object, Boolean> marked, Stack<Constant> 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}.
      */
--- 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();
+        }
+    }
 }