changeset 245:829b06307fb2

8013871: mem usage histograms enabled with compiler logging level set to more specific than or equals to info when --print-mem-usage flag is used Reviewed-by: jlaskey, hannesw
author lagergren
date Fri, 03 May 2013 16:01:33 +0200
parents 5a3f7867e19c
children c0f0033d7b08
files src/jdk/nashorn/internal/codegen/Compiler.java src/jdk/nashorn/internal/ir/debug/ClassHistogramElement.java src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java src/jdk/nashorn/internal/objects/Global.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/ScriptEnvironment.java src/jdk/nashorn/internal/runtime/options/Options.java src/jdk/nashorn/internal/runtime/resources/Options.properties src/jdk/nashorn/tools/Shell.java
diffstat 9 files changed, 626 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk/nashorn/internal/codegen/Compiler.java	Fri May 03 15:33:54 2013 +0200
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Fri May 03 16:01:33 2013 +0200
@@ -42,10 +42,13 @@
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -56,6 +59,8 @@
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
+import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
 import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
@@ -289,6 +294,40 @@
         this(env, null, sequence(env._lazy_compilation), env._strict);
     }
 
+    private static void printMemoryUsage(final String phaseName, final FunctionNode functionNode) {
+        final ObjectSizeCalculator osc = new ObjectSizeCalculator(ObjectSizeCalculator.getEffectiveMemoryLayoutSpecification());
+        osc.calculateObjectSize(functionNode);
+
+        final List<ClassHistogramElement> list = osc.getClassHistogram();
+
+        final StringBuilder sb = new StringBuilder();
+        final long totalSize = osc.calculateObjectSize(functionNode);
+        sb.append(phaseName).append(" Total size = ").append(totalSize / 1024 / 1024).append("MB");
+        LOG.info(sb);
+
+        Collections.sort(list, new Comparator<ClassHistogramElement>() {
+            @Override
+            public int compare(ClassHistogramElement o1, ClassHistogramElement o2) {
+                final long diff = o1.getBytes() - o2.getBytes();
+                if (diff < 0) {
+                    return 1;
+                } else if (diff > 0) {
+                    return -1;
+                } else {
+                    return 0;
+                }
+            }
+        });
+        for (final ClassHistogramElement e : list) {
+            final String line = String.format("    %-48s %10d bytes (%8d instances)", e.getClazz(), e.getBytes(), e.getInstances());
+            LOG.info(line);
+            if (e.getBytes() < totalSize / 20) {
+                LOG.info("    ...");
+                break; // never mind, so little memory anyway
+            }
+        }
+    }
+
     /**
      * Execute the compilation this Compiler was created with
      * @param functionNode function node to compile from its current state
@@ -312,6 +351,10 @@
         for (final CompilationPhase phase : sequence) {
             newFunctionNode = phase.apply(this, newFunctionNode);
 
+            if (env._print_mem_usage) {
+                printMemoryUsage(phase.toString(), newFunctionNode);
+            }
+
             final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
             time += duration;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/ir/debug/ClassHistogramElement.java	Fri May 03 16:01:33 2013 +0200
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.ir.debug;
+
+import java.util.Comparator;
+
+/**
+ * Class histogram element for IR / Java object instrumentation
+ */
+public class ClassHistogramElement {
+    /**
+     * Instance comparator
+     */
+    public static final Comparator<ClassHistogramElement> COMPARE_INSTANCES = new Comparator<ClassHistogramElement>() {
+        @Override
+        public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) {
+            return (int)Math.abs(o1.instances - o2.instances);
+        }
+    };
+
+    /**
+     * Bytes comparator
+     */
+    public static final Comparator<ClassHistogramElement> COMPARE_BYTES = new Comparator<ClassHistogramElement>() {
+        @Override
+        public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) {
+            return (int)Math.abs(o1.bytes - o2.bytes);
+        }
+    };
+
+    /**
+     * Classname comparator
+     */
+    public static final Comparator<ClassHistogramElement> COMPARE_CLASSNAMES = new Comparator<ClassHistogramElement>() {
+        @Override
+        public int compare(final ClassHistogramElement o1, final ClassHistogramElement o2) {
+            return o1.clazz.getCanonicalName().compareTo(o2.clazz.getCanonicalName());
+        }
+    };
+
+    private final Class<?> clazz;
+    private long instances;
+    private long bytes;
+
+    /**
+     * Constructor
+     * @param clazz class for which to construct histogram
+     */
+    public ClassHistogramElement(final Class<?> clazz) {
+        this.clazz = clazz;
+    }
+
+    /**
+     * Add an instance
+     * @param sizeInBytes byte count
+     */
+    public void addInstance(final long sizeInBytes) {
+        instances++;
+        this.bytes += sizeInBytes;
+    }
+
+    /**
+     * Get size in bytes
+     * @return size in bytes
+     */
+    public long getBytes() {
+        return bytes;
+    }
+
+    /**
+     * Get class
+     * @return class
+     */
+    public Class<?> getClazz() {
+        return clazz;
+    }
+
+    /**
+     * Get number of instances
+     * @return number of instances
+     */
+    public long getInstances() {
+        return instances;
+    }
+
+    @Override
+    public String toString() {
+        return "ClassHistogramElement[class=" + clazz.getCanonicalName() + ", instances=" + instances + ", bytes=" + bytes + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java	Fri May 03 16:01:33 2013 +0200
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.ir.debug;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains utility methods for calculating the memory usage of objects. It
+ * only works on the HotSpot JVM, and infers the actual memory layout (32 bit
+ * vs. 64 bit word size, compressed object pointers vs. uncompressed) from
+ * best available indicators. It can reliably detect a 32 bit vs. 64 bit JVM.
+ * It can only make an educated guess at whether compressed OOPs are used,
+ * though; specifically, it knows what the JVM's default choice of OOP
+ * compression would be based on HotSpot version and maximum heap sizes, but if
+ * the choice is explicitly overridden with the <tt>-XX:{+|-}UseCompressedOops</tt> command line
+ * switch, it can not detect
+ * this fact and will report incorrect sizes, as it will presume the default JVM
+ * behavior.
+ *
+ * @author Attila Szegedi
+ */
+public class ObjectSizeCalculator {
+
+    /**
+     * Describes constant memory overheads for various constructs in a JVM implementation.
+     */
+    public interface MemoryLayoutSpecification {
+
+        /**
+         * Returns the fixed overhead of an array of any type or length in this JVM.
+         *
+         * @return the fixed overhead of an array.
+         */
+        int getArrayHeaderSize();
+
+        /**
+         * Returns the fixed overhead of for any {@link Object} subclass in this JVM.
+         *
+         * @return the fixed overhead of any object.
+         */
+        int getObjectHeaderSize();
+
+        /**
+         * Returns the quantum field size for a field owned by an object in this JVM.
+         *
+         * @return the quantum field size for an object.
+         */
+        int getObjectPadding();
+
+        /**
+         * Returns the fixed size of an object reference in this JVM.
+         *
+         * @return the size of all object references.
+         */
+        int getReferenceSize();
+
+        /**
+         * Returns the quantum field size for a field owned by one of an object's ancestor superclasses
+         * in this JVM.
+         *
+         * @return the quantum field size for a superclass field.
+         */
+        int getSuperclassFieldPadding();
+    }
+
+    private static class CurrentLayout {
+        private static final MemoryLayoutSpecification SPEC =
+                getEffectiveMemoryLayoutSpecification();
+    }
+
+    /**
+     * Given an object, returns the total allocated size, in bytes, of the object
+     * and all other objects reachable from it.  Attempts to to detect the current JVM memory layout,
+     * but may fail with {@link UnsupportedOperationException};
+     *
+     * @param obj the object; can be null. Passing in a {@link java.lang.Class} object doesn't do
+     *          anything special, it measures the size of all objects
+     *          reachable through it (which will include its class loader, and by
+     *          extension, all other Class objects loaded by
+     *          the same loader, and all the parent class loaders). It doesn't provide the
+     *          size of the static fields in the JVM class that the Class object
+     *          represents.
+     * @return the total allocated size of the object and all other objects it
+     *         retains.
+     * @throws UnsupportedOperationException if the current vm memory layout cannot be detected.
+     */
+    public static long getObjectSize(final Object obj) throws UnsupportedOperationException {
+        return obj == null ? 0 : new ObjectSizeCalculator(CurrentLayout.SPEC).calculateObjectSize(obj);
+    }
+
+    // Fixed object header size for arrays.
+    private final int arrayHeaderSize;
+    // Fixed object header size for non-array objects.
+    private final int objectHeaderSize;
+    // Padding for the object size - if the object size is not an exact multiple
+    // of this, it is padded to the next multiple.
+    private final int objectPadding;
+    // Size of reference (pointer) fields.
+    private final int referenceSize;
+    // Padding for the fields of superclass before fields of subclasses are
+    // added.
+    private final int superclassFieldPadding;
+
+    private final Map<Class<?>, ClassSizeInfo> classSizeInfos = new IdentityHashMap<>();
+
+
+    private final Map<Object, Object> alreadyVisited = new IdentityHashMap<>();
+    private final Map<Class<?>, ClassHistogramElement> histogram = new IdentityHashMap<>();
+
+    private final Deque<Object> pending = new ArrayDeque<>(16 * 1024);
+    private long size;
+
+    /**
+     * Creates an object size calculator that can calculate object sizes for a given
+     * {@code memoryLayoutSpecification}.
+     *
+     * @param memoryLayoutSpecification a description of the JVM memory layout.
+     */
+    public ObjectSizeCalculator(final MemoryLayoutSpecification memoryLayoutSpecification) {
+        memoryLayoutSpecification.getClass();
+        arrayHeaderSize = memoryLayoutSpecification.getArrayHeaderSize();
+        objectHeaderSize = memoryLayoutSpecification.getObjectHeaderSize();
+        objectPadding = memoryLayoutSpecification.getObjectPadding();
+        referenceSize = memoryLayoutSpecification.getReferenceSize();
+        superclassFieldPadding = memoryLayoutSpecification.getSuperclassFieldPadding();
+    }
+
+    /**
+     * Given an object, returns the total allocated size, in bytes, of the object
+     * and all other objects reachable from it.
+     *
+     * @param obj the object; can be null. Passing in a {@link java.lang.Class} object doesn't do
+     *          anything special, it measures the size of all objects
+     *          reachable through it (which will include its class loader, and by
+     *          extension, all other Class objects loaded by
+     *          the same loader, and all the parent class loaders). It doesn't provide the
+     *          size of the static fields in the JVM class that the Class object
+     *          represents.
+     * @return the total allocated size of the object and all other objects it
+     *         retains.
+     */
+    public synchronized long calculateObjectSize(final Object obj) {
+        // Breadth-first traversal instead of naive depth-first with recursive
+        // implementation, so we don't blow the stack traversing long linked lists.
+        histogram.clear();
+        try {
+            for (Object o = obj;;) {
+                visit(o);
+                if (pending.isEmpty()) {
+                    return size;
+                }
+                o = pending.removeFirst();
+            }
+        } finally {
+            alreadyVisited.clear();
+            pending.clear();
+            size = 0;
+        }
+    }
+
+    /**
+     * Get the class histograpm
+     * @return class histogram element list
+     */
+    public List<ClassHistogramElement> getClassHistogram() {
+        return new ArrayList<>(histogram.values());
+    }
+
+    private ClassSizeInfo getClassSizeInfo(final Class<?> clazz) {
+        ClassSizeInfo csi = classSizeInfos.get(clazz);
+        if(csi == null) {
+            csi = new ClassSizeInfo(clazz);
+            classSizeInfos.put(clazz, csi);
+        }
+        return csi;
+    }
+
+    private void visit(final Object obj) {
+        if (alreadyVisited.containsKey(obj)) {
+            return;
+        }
+        final Class<?> clazz = obj.getClass();
+        if (clazz == ArrayElementsVisitor.class) {
+            ((ArrayElementsVisitor) obj).visit(this);
+        } else {
+            alreadyVisited.put(obj, obj);
+            if (clazz.isArray()) {
+                visitArray(obj);
+            } else {
+                getClassSizeInfo(clazz).visit(obj, this);
+            }
+        }
+    }
+
+    private void visitArray(final Object array) {
+        final Class<?> arrayClass = array.getClass();
+        final Class<?> componentType = arrayClass.getComponentType();
+        final int length = Array.getLength(array);
+        if (componentType.isPrimitive()) {
+            increaseByArraySize(arrayClass, length, getPrimitiveFieldSize(componentType));
+        } else {
+            increaseByArraySize(arrayClass, length, referenceSize);
+            // If we didn't use an ArrayElementsVisitor, we would be enqueueing every
+            // element of the array here instead. For large arrays, it would
+            // tremendously enlarge the queue. In essence, we're compressing it into
+            // a small command object instead. This is different than immediately
+            // visiting the elements, as their visiting is scheduled for the end of
+            // the current queue.
+            switch (length) {
+            case 0: {
+                break;
+            }
+            case 1: {
+                enqueue(Array.get(array, 0));
+                break;
+            }
+            default: {
+                enqueue(new ArrayElementsVisitor((Object[]) array));
+            }
+            }
+        }
+    }
+
+    private void increaseByArraySize(final Class<?> clazz, final int length, final long elementSize) {
+        increaseSize(clazz, roundTo(arrayHeaderSize + length * elementSize, objectPadding));
+    }
+
+    private static class ArrayElementsVisitor {
+        private final Object[] array;
+
+        ArrayElementsVisitor(final Object[] array) {
+            this.array = array;
+        }
+
+        public void visit(final ObjectSizeCalculator calc) {
+            for (final Object elem : array) {
+                if (elem != null) {
+                    calc.visit(elem);
+                }
+            }
+        }
+    }
+
+    void enqueue(final Object obj) {
+        if (obj != null) {
+            pending.addLast(obj);
+        }
+    }
+
+    void increaseSize(final Class<?> clazz, final long objectSize) {
+        ClassHistogramElement he = histogram.get(clazz);
+        if(he == null) {
+            he = new ClassHistogramElement(clazz);
+            histogram.put(clazz, he);
+        }
+        he.addInstance(objectSize);
+        size += objectSize;
+    }
+
+    static long roundTo(final long x, final int multiple) {
+        return ((x + multiple - 1) / multiple) * multiple;
+    }
+
+    private class ClassSizeInfo {
+        // Padded fields + header size
+        private final long objectSize;
+        // Only the fields size - used to calculate the subclasses' memory
+        // footprint.
+        private final long fieldsSize;
+        private final Field[] referenceFields;
+
+        public ClassSizeInfo(final Class<?> clazz) {
+            long newFieldsSize = 0;
+            final List<Field> newReferenceFields = new LinkedList<>();
+            for (Field f : clazz.getDeclaredFields()) {
+                if (Modifier.isStatic(f.getModifiers())) {
+                    continue;
+                }
+                final Class<?> type = f.getType();
+                if (type.isPrimitive()) {
+                    newFieldsSize += getPrimitiveFieldSize(type);
+                } else {
+                    f.setAccessible(true);
+                    newReferenceFields.add(f);
+                    newFieldsSize += referenceSize;
+                }
+            }
+            final Class<?> superClass = clazz.getSuperclass();
+            if (superClass != null) {
+                final ClassSizeInfo superClassInfo = getClassSizeInfo(superClass);
+                newFieldsSize += roundTo(superClassInfo.fieldsSize, superclassFieldPadding);
+                newReferenceFields.addAll(Arrays.asList(superClassInfo.referenceFields));
+            }
+            this.fieldsSize = newFieldsSize;
+            this.objectSize = roundTo(objectHeaderSize + newFieldsSize, objectPadding);
+            this.referenceFields = newReferenceFields.toArray(
+                    new Field[newReferenceFields.size()]);
+        }
+
+        void visit(final Object obj, final ObjectSizeCalculator calc) {
+            calc.increaseSize(obj.getClass(), objectSize);
+            enqueueReferencedObjects(obj, calc);
+        }
+
+        public void enqueueReferencedObjects(final Object obj, final ObjectSizeCalculator calc) {
+            for (Field f : referenceFields) {
+                try {
+                    calc.enqueue(f.get(obj));
+                } catch (IllegalAccessException e) {
+                    final AssertionError ae = new AssertionError(
+                            "Unexpected denial of access to " + f);
+                    ae.initCause(e);
+                    throw ae;
+                }
+            }
+        }
+    }
+
+    private static long getPrimitiveFieldSize(final Class<?> type) {
+        if (type == boolean.class || type == byte.class) {
+            return 1;
+        }
+        if (type == char.class || type == short.class) {
+            return 2;
+        }
+        if (type == int.class || type == float.class) {
+            return 4;
+        }
+        if (type == long.class || type == double.class) {
+            return 8;
+        }
+        throw new AssertionError("Encountered unexpected primitive type " +
+                type.getName());
+    }
+
+    /**
+     * Return the current memory usage
+     * @return current memory usage derived from system configuration
+     */
+    public static MemoryLayoutSpecification getEffectiveMemoryLayoutSpecification() {
+        final String vmName = System.getProperty("java.vm.name");
+        if (vmName == null || !vmName.startsWith("Java HotSpot(TM) ")) {
+            throw new UnsupportedOperationException(
+                    "ObjectSizeCalculator only supported on HotSpot VM");
+        }
+
+        final String dataModel = System.getProperty("sun.arch.data.model");
+        if ("32".equals(dataModel)) {
+            // Running with 32-bit data model
+            return new MemoryLayoutSpecification() {
+                @Override public int getArrayHeaderSize() {
+                    return 12;
+                }
+                @Override public int getObjectHeaderSize() {
+                    return 8;
+                }
+                @Override public int getObjectPadding() {
+                    return 8;
+                }
+                @Override public int getReferenceSize() {
+                    return 4;
+                }
+                @Override public int getSuperclassFieldPadding() {
+                    return 4;
+                }
+            };
+        } else if (!"64".equals(dataModel)) {
+            throw new UnsupportedOperationException("Unrecognized value '" +
+                    dataModel + "' of sun.arch.data.model system property");
+        }
+
+        final String strVmVersion = System.getProperty("java.vm.version");
+        final int vmVersion = Integer.parseInt(strVmVersion.substring(0,
+                strVmVersion.indexOf('.')));
+        if (vmVersion >= 17) {
+            long maxMemory = 0;
+            for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) {
+                maxMemory += mp.getUsage().getMax();
+            }
+            if (maxMemory < 30L * 1024 * 1024 * 1024) {
+                // HotSpot 17.0 and above use compressed OOPs below 30GB of RAM total
+                // for all memory pools (yes, including code cache).
+                return new MemoryLayoutSpecification() {
+                    @Override public int getArrayHeaderSize() {
+                        return 16;
+                    }
+                    @Override public int getObjectHeaderSize() {
+                        return 12;
+                    }
+                    @Override public int getObjectPadding() {
+                        return 8;
+                    }
+                    @Override public int getReferenceSize() {
+                        return 4;
+                    }
+                    @Override public int getSuperclassFieldPadding() {
+                        return 4;
+                    }
+                };
+            }
+        }
+
+        // In other cases, it's a 64-bit uncompressed OOPs object model
+        return new MemoryLayoutSpecification() {
+            @Override public int getArrayHeaderSize() {
+                return 24;
+            }
+            @Override public int getObjectHeaderSize() {
+                return 16;
+            }
+            @Override public int getObjectPadding() {
+                return 8;
+            }
+            @Override public int getReferenceSize() {
+                return 8;
+            }
+            @Override public int getSuperclassFieldPadding() {
+                return 8;
+            }
+        };
+    }
+}
--- a/src/jdk/nashorn/internal/objects/Global.java	Fri May 03 15:33:54 2013 +0200
+++ b/src/jdk/nashorn/internal/objects/Global.java	Fri May 03 16:01:33 2013 +0200
@@ -1625,7 +1625,6 @@
         this.addOwnProperty("Debug", Attribute.NOT_ENUMERABLE, initConstructor("Debug"));
     }
 
-    @SuppressWarnings("resource")
     private static Object printImpl(final boolean newLine, final Object... objects) {
         final PrintWriter out = Global.getEnv().getOut();
 
--- a/src/jdk/nashorn/internal/runtime/Context.java	Fri May 03 15:33:54 2013 +0200
+++ b/src/jdk/nashorn/internal/runtime/Context.java	Fri May 03 16:01:33 2013 +0200
@@ -171,7 +171,6 @@
      * @param str  text to write
      * @param crlf write a carriage return/new line after text
      */
-    @SuppressWarnings("resource")
     public static void err(final String str, final boolean crlf) {
         final PrintWriter err = Context.getCurrentErr();
         if (err != null) {
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Fri May 03 15:33:54 2013 +0200
+++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Fri May 03 16:01:33 2013 +0200
@@ -140,6 +140,9 @@
     /** Print resulting bytecode for script */
     public final boolean _print_code;
 
+    /** Print memory usage for IR after each phase */
+    public final boolean _print_mem_usage;
+
     /** Print function will no print newline characters */
     public final boolean _print_no_newline;
 
@@ -211,6 +214,7 @@
         _print_ast            = options.getBoolean("print.ast");
         _print_lower_ast      = options.getBoolean("print.lower.ast");
         _print_code           = options.getBoolean("print.code");
+        _print_mem_usage      = options.getBoolean("print.mem.usage");
         _print_no_newline     = options.getBoolean("print.no.newline");
         _print_parse          = options.getBoolean("print.parse");
         _print_lower_parse    = options.getBoolean("print.lower.parse");
--- a/src/jdk/nashorn/internal/runtime/options/Options.java	Fri May 03 15:33:54 2013 +0200
+++ b/src/jdk/nashorn/internal/runtime/options/Options.java	Fri May 03 16:01:33 2013 +0200
@@ -243,7 +243,7 @@
      */
     public String getString(final String key) {
         final Option<?> option = get(key);
-        if(option != null) {
+        if (option != null) {
             final String value = (String)option.getValue();
             if(value != null) {
                 return value.intern();
--- a/src/jdk/nashorn/internal/runtime/resources/Options.properties	Fri May 03 15:33:54 2013 +0200
+++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties	Fri May 03 16:01:33 2013 +0200
@@ -247,6 +247,12 @@
     desc="Print bytecode."    \
 }
 
+nashorn.option.print.mem.usage = {                            \
+    name="--print-mem-usage",                                 \
+    is_undocumented=true,                                     \
+    desc="Print memory usage of IR after each compile stage." \
+}
+
 nashorn.option.print.no.newline = {                     \
     name="--print-no-newline",                          \
     is_undocumented=true,                               \
--- a/src/jdk/nashorn/tools/Shell.java	Fri May 03 15:33:54 2013 +0200
+++ b/src/jdk/nashorn/tools/Shell.java	Fri May 03 16:01:33 2013 +0200
@@ -173,9 +173,9 @@
 
         if (env._fx) {
             return runFXScripts(context, global, files);
-        } else {
-            return runScripts(context, global, files);
         }
+
+        return runScripts(context, global, files);
     }
 
     /**