diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
index 30a134642b932dbd8197084dfad5f5562ecdf335..04c66c9f1e98f0a38d1966e699a4142dfe2392ad 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
@@ -242,7 +242,7 @@ public class RCommand {
                             } else if (cause instanceof ExitException) {
                                 // usually from quit
                                 vm.dispose();
-                                System.exit(((ExitException) cause).getStatus());
+                                Utils.systemExit(((ExitException) cause).getStatus());
                             } else {
                                 RInternalError.reportErrorAndConsoleLog(cause, consoleHandler, 0);
                                 // We continue the repl even though the system may be broken
@@ -262,7 +262,7 @@ public class RCommand {
             } catch (Throwable e) {
                 if (e.getCause() instanceof ExitException) {
                     // normal quit, but with exit code based on lastStatus
-                    System.exit(lastStatus);
+                    Utils.systemExit(lastStatus);
                 }
                 throw RInternalError.shouldNotReachHere(e);
             }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java
index 5b4d80c96f3f3d8b085cf7f2a706ebb056a7eba9..b952a0da7a3b74f43725f163a7221b7d57d48379 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java
@@ -22,199 +22,56 @@
  */
 package com.oracle.truffle.r.library.utils;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.library.utils.ObjectSizeNodeGen.RecursiveObjectSizeNodeGen;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributes;
-import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RExpression;
+import com.oracle.truffle.r.runtime.data.RObjectSize;
 import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
 
-/*
- * Similarly to GNU R's version, this is very approximate
- * (e.g. overhead related to Java object headers is not included)
- * and is only (semi) accurate for atomic vectors.
+/**
+ * Similarly to GNU R's version, this is approximate and based, for {@link RTypedValue} instances on
+ * {@link RObjectSize#getObjectSize}. As per GNU R the AST size for a closure is included. TODO AST
+ * size not included owing to problems sizing it automatically.
  */
+@SuppressWarnings("unused")
 public abstract class ObjectSize extends RExternalBuiltinNode.Arg1 {
 
-    protected abstract int executeInt(Object o);
-
-    @Child RecursiveObjectSize recursiveObjectSize;
-
-    protected int recursiveObjectSize(Object o) {
-        if (recursiveObjectSize == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            recursiveObjectSize = insert(RecursiveObjectSizeNodeGen.create());
-        }
-        return recursiveObjectSize.executeInt(o);
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RNull o) {
-        return 64; // pointer?
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") int o) {
-        return 32;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") double o) {
-        return 64;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") byte o) {
-        return 8;
-    }
-
-    @Specialization
-    protected int objectSize(String o) {
-        return o.length() * 16;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RRaw o) {
-        return 8;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RComplex o) {
-        return 128;
-    }
-
-    @Specialization
-    protected int objectSize(RIntSequence o) {
-        int res = 96; // int length + int start + int stride
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RDoubleSequence o) {
-        int res = 160; // int length + double start + double stride
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RIntVector o) {
-        return o.getLength() * 32 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RDoubleVector o) {
-        return o.getLength() * 64 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RStringVector o) {
-        int res = 0;
-        for (int i = 0; i < o.getLength(); i++) {
-            res += o.getLength() * 16;
-        }
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RLogicalVector o) {
-        return o.getLength() * 8 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RComplexVector o) {
-        return o.getLength() * 128 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RRawVector o) {
-        return o.getLength() * 8 + attrSize(o);
-    }
-
-    @Specialization
-    @TruffleBoundary
-    protected int objectSize(RList o) {
-        int res = 0;
-        for (int i = 0; i < o.getLength(); i++) {
-            res += recursiveObjectSize(o.getDataAt(i));
-        }
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    @TruffleBoundary
-    protected int objectSize(RPairList o) {
-        RPairList list = o;
-        Object car = list.car();
-        int res = 0;
-        while (true) {
-            res += recursiveObjectSize(car);
-            Object cdr = list.cdr();
-            if (cdr == RNull.instance) {
-                break;
+    private static class MyIgnoreObjectHandler implements RObjectSize.IgnoreObjectHandler {
+        @Override
+        public boolean ignore(Object rootObject, Object obj) {
+            if (obj == RNull.instance) {
+                return true;
             } else {
-                list = (RPairList) cdr;
-                car = list.car();
+                return false;
             }
         }
-        return res + attrSize(o);
-    }
 
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RFunction o) {
-        return 256; // arbitrary, but does it really matter?
     }
 
+    private static final MyIgnoreObjectHandler ignoreObjectHandler = new MyIgnoreObjectHandler();
+
     @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RLanguage o) {
-        return 256; // arbitrary, but does it really matter?
+    protected int objectSize(int o) {
+        return RObjectSize.INT_SIZE;
     }
 
     @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RExpression o) {
-        return 256; // arbitrary, but does it really matter?
+    protected int objectSize(double o) {
+        return RObjectSize.DOUBLE_SIZE;
     }
 
-    protected int attrSize(RAttributable o) {
-        return o.getAttributes() == null ? 0 : attrSizeInternal(o.getAttributes());
+    @Specialization
+    protected int objectSize(byte o) {
+        return RObjectSize.BYTE_SIZE;
     }
 
+    @Fallback
     @TruffleBoundary
-    protected int attrSizeInternal(RAttributes attributes) {
-        int size = 0;
-        for (RAttribute attr : attributes) {
-            size += attr.getName().length() * 16;
-            size += recursiveObjectSize(attr.getValue());
-        }
-        return size;
-    }
-
-    protected abstract static class RecursiveObjectSize extends TruffleBoundaryNode {
-
-        protected abstract int executeInt(Object o);
-
-        @Child ObjectSize objectSize = ObjectSizeNodeGen.create();
-
-        @Specialization
-        protected int objectSize(Object o) {
-            return objectSize.executeInt(o);
-        }
+    protected int objectSize(Object o) {
+        return (int) RObjectSize.getObjectSize(o, ignoreObjectHandler);
     }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java
index d03f663b1151eaae9a39bdecede4f62e9996a80e..db38227316cc29e6eec6064ae342363e887c6018 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java
@@ -50,12 +50,36 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RObjectSize;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState;
+import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState.MemoryQuad;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
+/**
+ * Implements the {@code Rprof} external.
+ *
+ * The output is basically a sequence of call stacks, output at each sample interval, with entries
+ * in the stack identified by quoted function names. If memory profiling, the stack is preceded by a
+ * auad of numbers {@code :smallv:bigv:nodes:duplicate_counter:} allocated in the interval. If line
+ * profiling is enabled source files are listed as
+ *
+ * <pre>
+ * #File N: path
+ * </pre>
+ *
+ * and then the {@code N} is used in line number references of the form {@code N#L},which precede
+ * the function name.
+ *
+ */
+public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFactory.Listener, MemoryCopyTracer.Listener {
+
+    private RprofState profState;
 
     @SuppressWarnings("unused")
     @Specialization
@@ -64,13 +88,13 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
         if (!RContext.getInstance().isInitial()) {
             throw RError.error(this, RError.Message.GENERIC, "profiling not supported in created contexts");
         }
-        RprofState profState = RContext.getInstance().stateInstrumentation.getRprof();
+        profState = RContext.getInstance().stateInstrumentation.getRprof();
         String filename = filenameVec.getDataAt(0);
         if (filename.length() == 0) {
             // disable
             endProfiling();
         } else {
-            // enable
+            // enable after ending any previous session
             if (profState.out() != null) {
                 endProfiling();
             }
@@ -79,18 +103,21 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
             boolean gcProfiling = RRuntime.fromLogical(gcProfilingL);
             try {
                 PrintWriter out = new PrintWriter(new FileWriter(filename, append));
-                if (memProfiling) {
-                    RError.warning(this, RError.Message.GENERIC, "Rprof: memory profiling not supported");
-                }
                 if (gcProfiling) {
                     RError.warning(this, RError.Message.GENERIC, "Rprof: gc profiling not supported");
                 }
+                if (memProfiling) {
+                    RDataFactory.addListener(this);
+                    RDataFactory.setAllocationTracing(true);
+                    MemoryCopyTracer.addListener(this);
+                    MemoryCopyTracer.setTracingState(true);
+                }
                 // interval is in seconds, we convert to millis
                 long intervalInMillis = (long) (1E3 * intervalD);
                 StatementListener statementListener = new StatementListener();
                 ProfileThread profileThread = new ProfileThread(intervalInMillis, statementListener);
                 profileThread.setDaemon(true);
-                profState.initialize(out, profileThread, statementListener, intervalInMillis, RRuntime.fromLogical(lineProfilingL));
+                profState.initialize(out, profileThread, statementListener, intervalInMillis, RRuntime.fromLogical(lineProfilingL), memProfiling);
                 profileThread.start();
             } catch (IOException ex) {
                 throw RError.error(this, RError.Message.GENERIC, String.format("Rprof: cannot open profile file '%s'", filename));
@@ -99,13 +126,37 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
         return RNull.instance;
     }
 
-    private static void endProfiling() {
-        RprofState profState = RContext.getInstance().stateInstrumentation.getRprof();
+    @Override
+    @TruffleBoundary
+    public void reportAllocation(RTypedValue data) {
+        long size = RObjectSize.getObjectSize(data, Rprofmem.myIgnoreObjectHandler);
+        if (data instanceof RAbstractVector) {
+            if (size >= Rprofmem.LARGE_VECTOR) {
+                profState.memoryQuad().largeV += size;
+            } else {
+                profState.memoryQuad().smallV += size;
+            }
+        } else {
+            profState.memoryQuad().nodes += size;
+        }
+
+    }
+
+    @Override
+    @TruffleBoundary
+    public void reportCopying(RAbstractVector source, RAbstractVector dest) {
+        profState.memoryQuad().copied += RObjectSize.getObjectSize(source, Rprofmem.myIgnoreObjectHandler);
+    }
+
+    private void endProfiling() {
         ProfileThread profileThread = (ProfileThread) profState.profileThread();
         profileThread.running = false;
         HashMap<String, Integer> fileMap = null;
         PrintWriter out = profState.out();
         StatementListener statementListener = (StatementListener) profState.statementListener();
+        if (profState.memoryProfiling()) {
+            out.print("memory profiling: ");
+        }
         if (profState.lineProfiling()) {
             out.print("line profiling: ");
         }
@@ -124,7 +175,12 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
                 }
             }
         }
+        int index = 0;
         for (ArrayList<RSyntaxNode> intervalStack : statementListener.intervalStacks) {
+            if (profState.memoryProfiling()) {
+                MemoryQuad mq = statementListener.intervalMemory.get(index);
+                out.printf(":%d:%d:%d:%d:", mq.largeV, mq.smallV, mq.nodes, mq.copied);
+            }
             for (RSyntaxNode node : intervalStack) {
                 RootNode rootNode = node.asRNode().getRootNode();
                 if (rootNode instanceof FunctionDefinitionNode) {
@@ -139,8 +195,15 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
                 }
             }
             out.println();
+            index++;
         }
         out.close();
+        profState.setOut(null);
+        if (profState.memoryProfiling()) {
+            RDataFactory.setAllocationTracing(false);
+            MemoryCopyTracer.setTracingState(false);
+        }
+
     }
 
     private static String getPath(RSyntaxNode node) {
@@ -177,8 +240,9 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
      * Emulates a sampling timer by checking when the sample interval rolls over and at that point
      * collects the stack of functions.
      */
-    private static final class StatementListener implements ExecutionEventListener {
+    private final class StatementListener implements ExecutionEventListener {
         private ArrayList<ArrayList<RSyntaxNode>> intervalStacks = new ArrayList<>();
+        private ArrayList<MemoryQuad> intervalMemory = new ArrayList<>();
         private volatile boolean newInterval;
 
         private StatementListener() {
@@ -200,12 +264,16 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
                 stack.add((RSyntaxNode) context.getInstrumentedNode());
                 collectStack(stack);
                 intervalStacks.add(stack);
+                if (profState.memoryProfiling()) {
+                    intervalMemory.add(profState.memoryQuad().copyAndClear());
+                }
+
                 newInterval = false;
             }
         }
 
         @TruffleBoundary
-        private static void collectStack(final ArrayList<RSyntaxNode> stack) {
+        private void collectStack(final ArrayList<RSyntaxNode> stack) {
             Utils.iterateRFrames(FrameAccess.READ_ONLY, new Function<Frame, Object>() {
 
                 @Override
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fa5c672e50bcf65325c39085cb018110c0dc822
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016, 2016, 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.
+ *
+ * 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 com.oracle.truffle.r.library.utils;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RObjectSize;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofmemState;
+
+public abstract class Rprofmem extends RExternalBuiltinNode.Arg3 implements RDataFactory.Listener {
+
+    private RprofmemState profmemState;
+
+    @Specialization
+    @TruffleBoundary
+    public Object doRprofmem(RAbstractStringVector filenameVec, byte appendL, RAbstractDoubleVector thresholdVec) {
+        if (!RContext.getInstance().isInitial()) {
+            throw RError.error(this, RError.Message.GENERIC, "profiling not supported in created contexts");
+        }
+        String filename = filenameVec.getDataAt(0);
+        if (filename.length() == 0) {
+            // disable
+            endProfiling();
+        } else {
+            // enable after ending any previous session
+            profmemState = RContext.getInstance().stateInstrumentation.getRprofmem();
+            if (profmemState.out() != null) {
+                endProfiling();
+            }
+            boolean append = RRuntime.fromLogical(appendL);
+            try {
+                PrintWriter out = new PrintWriter(new FileWriter(filename, append));
+                profmemState.initialize(out, thresholdVec.getDataAt(0));
+                RDataFactory.addListener(this);
+                RDataFactory.setAllocationTracing(true);
+            } catch (IOException ex) {
+                throw RError.error(this, RError.Message.GENERIC, String.format("Rprofmem: cannot open profile file '%s'", filename));
+            }
+        }
+        return RNull.instance;
+    }
+
+    private void endProfiling() {
+        if (profmemState != null) {
+            RDataFactory.setAllocationTracing(false);
+            profmemState.out().flush();
+            profmemState.out().close();
+            profmemState.setOut(null);
+        }
+    }
+
+    private static final int PAGE_SIZE = 2000;
+    static final int LARGE_VECTOR = 128;
+
+    /**
+     * We ignore nested {@link RTypedValue} instances as these will have been counted already. We
+     * also ignore {@link Node} instances, except in {@link RFunction} objects.
+     */
+    private static class MyIgnoreObjectHandler implements RObjectSize.IgnoreObjectHandler {
+        @Override
+        public boolean ignore(Object rootObject, Object obj) {
+            if (obj == RNull.instance) {
+                return true;
+            } else {
+                Class<?> klass = obj.getClass();
+                if (RTypedValue.class.isAssignableFrom(klass)) {
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+        }
+
+    }
+
+    static final RObjectSize.IgnoreObjectHandler myIgnoreObjectHandler = new MyIgnoreObjectHandler();
+
+    @Override
+    @TruffleBoundary
+    public void reportAllocation(RTypedValue data) {
+        // We could do some in memory buffering
+        // TODO write out full stack
+        Frame frame = Utils.getActualCurrentFrame();
+        if (frame == null) {
+            // not an R evaluation, some internal use
+            return;
+        }
+        RFunction func = RArguments.getFunction(frame);
+        if (func == null) {
+            return;
+        }
+        String name = func.getRootNode().getName();
+
+        long size = RObjectSize.getObjectSize(data, myIgnoreObjectHandler);
+        if (data instanceof RAbstractVector && size >= LARGE_VECTOR) {
+            profmemState.out().printf("%d: %s\n", size, name);
+        } else {
+            int pageCount = profmemState.pageCount();
+            long pcs = pageCount + size;
+            if (pcs > PAGE_SIZE) {
+                profmemState.out().printf("new page: %s\n", name);
+                profmemState.setPageCount((int) (pcs - PAGE_SIZE));
+            } else {
+                profmemState.setPageCount((int) pcs);
+            }
+        }
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
index d294b653a4db4d9fc01226325f0dca790b9db417..13c44d60cfdd6e5a75207c2b9d794f8507b106a1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
@@ -54,7 +54,7 @@ import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.MemoryTracer;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -79,7 +79,7 @@ public class TraceFunctions {
     public abstract static class PrimTrace extends Helper {
 
         @Specialization
-        protected RNull primUnTrace(VirtualFrame frame, RAbstractStringVector funcName) {
+        protected RNull primTrace(VirtualFrame frame, RAbstractStringVector funcName) {
             return primTrace((RFunction) getFunction(frame, funcName.getDataAt(0)));
         }
 
@@ -118,10 +118,12 @@ public class TraceFunctions {
         @Specialization
         @TruffleBoundary
         protected byte traceOnOff(byte state) {
+            /* TODO GnuR appears to accept ANY value as an argument */
             boolean prevState = RContext.getInstance().stateInstrumentation.getTracingState();
             boolean newState = RRuntime.fromLogical(state);
             if (newState != prevState) {
                 RContext.getInstance().stateInstrumentation.setTracingState(newState);
+                MemoryCopyTracer.setTracingState(newState);
             }
             return RRuntime.asLogical(prevState);
         }
@@ -135,7 +137,7 @@ public class TraceFunctions {
 
     public abstract static class TracememBase extends RBuiltinNode {
         static {
-            MemoryTracer.setListener(new TracememBase.TracememListener());
+            MemoryCopyTracer.addListener(new TracememBase.TracememListener());
         }
 
         protected static HashSet<Object> getTracedObjects() {
@@ -147,8 +149,16 @@ public class TraceFunctions {
         }
 
         protected static void startTracing(Object x) {
+            /*
+             * There is no explicit command to enable tracing, it is implicit in the call to
+             * tracemem. However, it can be disabled by tracingState(F), so we can't unilaterally
+             * turn on tracing here.
+             */
             getTracedObjects().add(x);
-            MemoryTracer.reportEvents();
+            boolean tracingState = RContext.getInstance().stateInstrumentation.getTracingState();
+            if (tracingState) {
+                MemoryCopyTracer.setTracingState(true);
+            }
         }
 
         protected static void printToStdout(String msg) {
@@ -176,7 +186,7 @@ public class TraceFunctions {
             return result.toString();
         }
 
-        private static final class TracememListener implements MemoryTracer.Listener {
+        private static final class TracememListener implements MemoryCopyTracer.Listener {
             @Override
             public void reportCopying(RAbstractVector src, RAbstractVector dest) {
                 if (getTracedObjects().contains(src)) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
index 582337c61d1c2c1eff10889bcbff2597c3761af0..624b0baa010ced6e4ec76b6d3e99758a2bff94c1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
@@ -66,6 +66,7 @@ import com.oracle.truffle.r.library.utils.Download;
 import com.oracle.truffle.r.library.utils.MenuNodeGen;
 import com.oracle.truffle.r.library.utils.ObjectSizeNodeGen;
 import com.oracle.truffle.r.library.utils.RprofNodeGen;
+import com.oracle.truffle.r.library.utils.RprofmemNodeGen;
 import com.oracle.truffle.r.library.utils.TypeConvertNodeGen;
 import com.oracle.truffle.r.library.utils.WriteTable;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
@@ -576,8 +577,9 @@ public class ForeignFunctions {
                     return getExternalModelBuiltinNode("termsform");
                 case "Rprof":
                     return RprofNodeGen.create();
-                case "unzip":
                 case "Rprofmem":
+                    return RprofmemNodeGen.create();
+                case "unzip":
                 case "addhistory":
                 case "loadhistory":
                 case "savehistory":
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index 8733ab397016e835599532c18d617f86ab877e7b..486c4b98687d8b2f54e026a3d432d2ecdb9cd7c0 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -169,6 +169,18 @@ public final class Utils {
         throw new ExitException(2);
     }
 
+    /**
+     * This the real, final, non-overrideable, exit of the entire R system. TODO well, modulo how
+     * quit() is interpreted when R is started implicitly from a Polyglot shell that is running
+     * other languages.
+     *
+     * @param status
+     */
+    public static void systemExit(int status) {
+        RPerfStats.report();
+        System.exit(status);
+    }
+
     private static String userHome;
 
     private static String userHome() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e303054edb4f818d2153f011bac4e37a0baebff
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2016, 2016, 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.
+ *
+ * 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 com.oracle.truffle.r.runtime.data;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import javax.tools.ToolProvider;
+
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler;
+import com.oracle.truffle.r.runtime.data.RObjectSize.TypeCustomizer;
+
+/**
+ * Uses an instrumentation agent to get an accurate estimate of an objects size, plus reflection to
+ * aggregate the size of object-valued fields. Sharing is not handled in the general case, although
+ * some special cases are handed, such as the fact that {@link RNull} is a singleton.
+ *
+ * In order to satisfy the requirements of the Java instrumentation API, we have to load the agent
+ * from a jar file. The creation and loading is all orchestrated by this class.
+ */
+public class AgentObjectSizeFactory extends ObjectSizeFactory {
+
+    private Map<Class<?>, ArrayList<Field>> objectFieldsMap = new HashMap<>();
+    private static Map<Class<?>, TypeCustomizer> customizerMap = new HashMap<>(); // system wide
+
+    public AgentObjectSizeFactory() {
+        if (!ObjSizeAgent.isInitialized()) {
+            try {
+                createAgentJar();
+            } catch (Exception ex) {
+                // not available
+                Utils.rSuicide("failed to load ObjSizeAgent: " + ex.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Adds the class file bytes for a given class to a JAR stream.
+     */
+    static void add(JarOutputStream jar, Class<?> c) throws IOException {
+        String name = c.getName();
+        String classAsPath = name.replace('.', '/') + ".class";
+        jar.putNextEntry(new JarEntry(classAsPath));
+
+        InputStream stream = c.getClassLoader().getResourceAsStream(classAsPath);
+
+        int nRead;
+        byte[] buf = new byte[1024];
+        while ((nRead = stream.read(buf, 0, buf.length)) != -1) {
+            jar.write(buf, 0, nRead);
+        }
+
+        jar.closeEntry();
+    }
+
+    protected void createAgentJar() throws Exception {
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        Attributes mainAttrs = manifest.getMainAttributes();
+        mainAttrs.putValue("Agent-Class", ObjSizeAgent.class.getName());
+        mainAttrs.putValue("Premain-Class", ObjSizeAgent.class.getName());
+
+        Path jar = Files.createTempFile("myagent", ".jar");
+        try {
+            JarOutputStream jarStream = new JarOutputStream(new FileOutputStream(jar.toFile()), manifest);
+            add(jarStream, ObjSizeAgent.class);
+            jarStream.close();
+
+            loadAgent(jar);
+        } finally {
+            Files.deleteIfExists(jar);
+        }
+    }
+
+    public static void loadAgent(Path agent) throws Exception {
+        String vmName = ManagementFactory.getRuntimeMXBean().getName();
+        int p = vmName.indexOf('@');
+        String pid = vmName.substring(0, p);
+        ClassLoader cl = ToolProvider.getSystemToolClassLoader();
+        Class<?> c = Class.forName("com.sun.tools.attach.VirtualMachine", true, cl);
+        Method attach = c.getDeclaredMethod("attach", String.class);
+        Method loadAgent = c.getDeclaredMethod("loadAgent", String.class, String.class);
+        Object vm = attach.invoke(null, pid);
+        loadAgent.invoke(vm, agent.toString(), "");
+    }
+
+    @Override
+    public long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        return getObjectSize(obj, obj, ignoreObjectHandler);
+    }
+
+    private long getObjectSize(Object rootObj, Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        try {
+            long basicSize = ObjSizeAgent.objectSize(obj);
+            long size = basicSize;
+            Class<?> klass = obj.getClass();
+            if (klass.isArray() && !klass.getComponentType().isPrimitive()) {
+                for (int i = 0; i < Array.getLength(obj); i++) {
+                    Object elem = Array.get(obj, i);
+                    if (elem == null || isNa(elem)) {
+                        continue;
+                    } else {
+                        size += getObjectSize(rootObj, elem, ignoreObjectHandler);
+                    }
+                }
+            } else {
+                ArrayList<Field> objectFields = objectFieldsMap.get(klass);
+                if (objectFields == null) {
+                    objectFields = new ArrayList<>();
+                    findObjectFields(obj.getClass(), objectFields);
+                    objectFieldsMap.put(klass, objectFields);
+                }
+                for (Field objectField : objectFields) {
+                    Object fieldObj = objectField.get(obj);
+                    if (fieldObj == null || ignoreObjectHandler.ignore(rootObj, fieldObj)) {
+                        continue;
+                    } else {
+                        TypeCustomizer typeCustomizer = getCustomizer(fieldObj.getClass());
+                        if (typeCustomizer == null) {
+                            size += getObjectSize(rootObj, fieldObj, ignoreObjectHandler);
+                        } else {
+                            size += typeCustomizer.getObjectSize(fieldObj);
+                        }
+                    }
+                }
+            }
+            return size;
+        } catch (Throwable t) {
+            throw RInternalError.shouldNotReachHere(t);
+        }
+
+    }
+
+    private static boolean isNa(Object elem) {
+        String typeName = elem.getClass().getSimpleName();
+        switch (typeName) {
+            case "Integer":
+                return RRuntime.isNA((int) elem);
+            case "Double":
+                return RRuntime.isNA((double) elem);
+            case "String":
+                return RRuntime.isNA((String) elem);
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Walks the superclass hierarchy of {@code klass} and accumulates all object-valued fields in
+     * {@code objectFields}.
+     */
+    private static void findObjectFields(Class<?> klass, ArrayList<Field> objectFields) {
+        if (klass != Object.class) {
+            findObjectFields(klass.getSuperclass(), objectFields);
+            Field[] fields = klass.getDeclaredFields();
+            for (Field field : fields) {
+                Class<?> fieldClass = field.getType();
+                if (fieldClass.isPrimitive()) {
+                    continue;
+                }
+                int modifiers = field.getModifiers();
+                if (Modifier.isStatic(modifiers)) {
+                    continue;
+                }
+                // check for special case of an completely ignored type
+                if (getCustomizer(fieldClass) == RObjectSize.IGNORE) {
+                    continue;
+                }
+                field.setAccessible(true);
+                objectFields.add(field);
+            }
+        }
+    }
+
+    private static TypeCustomizer getCustomizer(Class<?> objClass) {
+        for (Map.Entry<Class<?>, TypeCustomizer> entry : customizerMap.entrySet()) {
+            if (entry.getKey().isAssignableFrom(objClass)) {
+                return entry.getValue();
+            }
+        }
+        return null;
+
+    }
+
+    @Override
+    public void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer) {
+        customizerMap.put(klass, typeCustomizer);
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java
similarity index 56%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java
index 05b5db6c2ab0659d63d734052b21dd3216ae1553..f6e7cabae776ea3598ae3034f6ee4da2ee799ca1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java
@@ -23,48 +23,56 @@
 
 package com.oracle.truffle.r.runtime.data;
 
-import com.oracle.truffle.api.Assumption;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.r.runtime.context.RContext;
+import java.util.Deque;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.utilities.CyclicAssumption;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 /**
- * Helper for tracing memory related events. All implementors of {@link RAbstractVector} are
- * expected to report to {@link MemoryTracer} and others can listen to them through {@link Listener}
- * interface. Use method {@link #reportEvents()} to start the tracing.
+ * Helper for tracing memory copying events, as used by the {@code tracemem} bultin. All
+ * implementors of {@link RAbstractVector} are expected to report to {@link MemoryCopyTracer} and
+ * others can listen to them through {@link Listener} interface. Use method
+ * {@link #setTracingState(boolean)} to enable/disable the tracing.
  */
-public final class MemoryTracer {
-    private static Listener listener;
-    private static final Assumption noMemoryTracingAssumption = Truffle.getRuntime().createAssumption();
+public final class MemoryCopyTracer {
+    private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>();
+    @CompilationFinal private static boolean enabled;
+
+    private static final CyclicAssumption noMemoryCopyTracingAssumption = new CyclicAssumption("data copying");
 
-    private MemoryTracer() {
+    private MemoryCopyTracer() {
         // only static methods
     }
 
     /**
-     * Sets the listener of memory tracing events. For the time being there can only be one
-     * listener. This can be extended to an array should we need more listeners.
+     * Adds a listener of memory copying events.
      */
-    public static void setListener(Listener newListener) {
-        listener = newListener;
+    public static void addListener(Listener listener) {
+        listeners.addLast(listener);
     }
 
     /**
      * After calling this method memory related events will be reported to the listener. This
      * invalidates global assumption and should be used with caution.
      */
-    public static void reportEvents() {
-        noMemoryTracingAssumption.invalidate();
+    public static void setTracingState(boolean newState) {
+        if (enabled != newState) {
+            noMemoryCopyTracingAssumption.invalidate();
+            enabled = newState;
+        }
     }
 
     /**
      * Reports copy event to the listener. If there are no traced objects, this should turn into
-     * no-op. TODO might be worth interposing on a change in {@code tracingState} to turn off the
-     * collection.
+     * no-op.
      */
     public static void reportCopying(RAbstractVector source, RAbstractVector dest) {
-        if (!noMemoryTracingAssumption.isValid() && listener != null && RContext.getInstance().stateInstrumentation.getTracingState()) {
-            listener.reportCopying(source, dest);
+        if (enabled) {
+            for (Listener listener : listeners) {
+                listener.reportCopying(source, dest);
+            }
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6756248470bea75bf369bfd2859bd071c11de48
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016, 2016, 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.
+ *
+ * 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 com.oracle.truffle.r.runtime.data;
+
+import java.lang.instrument.Instrumentation;
+
+/**
+ * This is agent class for object sizing. It has to be separate as it is loaded from a jar file.
+ * This implements the basic call to the JVM for the "struct" part of the object.
+ * {@link AgentObjectSizeFactory} handles the recursive sizing based on the field/array types.
+ *
+ */
+public class ObjSizeAgent {
+    private static Instrumentation instrumentation;
+
+    public static void premain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) {
+        instrumentation = inst;
+    }
+
+    public static void agentmain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) {
+        instrumentation = inst;
+    }
+
+    public static long objectSize(Object obj) {
+        return instrumentation.getObjectSize(obj);
+    }
+
+    static boolean isInitialized() {
+        return instrumentation != null;
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..18a0cb472db916172c7386ab5e62fb9334a0552f
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016, 2016, 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.
+ *
+ * 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 com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler;
+import com.oracle.truffle.r.runtime.data.RObjectSize.TypeCustomizer;
+
+public abstract class ObjectSizeFactory {
+
+    static {
+        final String prop = System.getProperty("fastr.objectsize.factory.class", "com.oracle.truffle.r.runtime.data.AgentObjectSizeFactory");
+        try {
+            theInstance = (ObjectSizeFactory) Class.forName(prop).newInstance();
+        } catch (Exception ex) {
+            // CheckStyle: stop system..print check
+            System.err.println("Failed to instantiate class: " + prop);
+        }
+    }
+
+    private static ObjectSizeFactory theInstance;
+
+    public static ObjectSizeFactory getInstance() {
+        return theInstance;
+    }
+
+    /**
+     * See {@link RObjectSize#getObjectSize}.
+     */
+    public abstract long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler);
+
+    public abstract void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer);
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d7279743c01e4518d20124db73a4676a5c65a0a
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016, 2016, 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.
+ *
+ * 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 com.oracle.truffle.r.runtime.data;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler;
+
+/**
+ * A debugging tool, logs all calls to {@link #getObjectSize(Object, IgnoreObjectHandler)} to a
+ * file.
+ *
+ */
+public class OutputAgentObjectSizeFactory extends AgentObjectSizeFactory {
+
+    private PrintWriter printWriter;
+
+    public OutputAgentObjectSizeFactory() {
+        try {
+            printWriter = new PrintWriter(new FileWriter(Utils.getLogPath("fastr_objectsize.log").toString()));
+        } catch (IOException ex) {
+            Utils.rSuicide(ex.getMessage());
+        }
+
+    }
+
+    @Override
+    public long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        long size = super.getObjectSize(obj, ignoreObjectHandler);
+        printWriter.printf("%s: %d\n", obj.getClass().getSimpleName(), (int) size);
+        printWriter.flush();
+        return size;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java
index 1bfe70252de40c3162543da9ef30c416b27154b2..430be1e43025c645c81ffbb1c59aa6f756c8bee6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java
@@ -68,7 +68,7 @@ public final class RComplex extends RScalarVector implements RAbstractComplexVec
     @Override
     public RComplexVector materialize() {
         RComplexVector result = RDataFactory.createComplexVectorFromScalar(this);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
index 6131402db4e6ecb0b06453b5f497cb289ce5314e..dad4afd45a6e0e52823f4e946f459dd9a5514e1e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
@@ -23,8 +23,10 @@
 package com.oracle.truffle.r.runtime.data;
 
 import java.util.Arrays;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.oracle.truffle.api.Assumption;
@@ -32,7 +34,7 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.frame.MaterializedFrame;
-import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.utilities.CyclicAssumption;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -49,11 +51,6 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 
 public final class RDataFactory {
 
-    /**
-     * Profile for creation tracing; must precede following declarations.
-     */
-    private static final ConditionProfile statsProfile = ConditionProfile.createBinaryProfile();
-
     public static final boolean INCOMPLETE_VECTOR = false;
     public static final boolean COMPLETE_VECTOR = true;
 
@@ -498,24 +495,79 @@ public final class RDataFactory {
         return traceDataCreated(new RExternalPtr(value, tag, RNull.instance));
     }
 
-    @CompilationFinal private static PerfHandler stats;
+    /*
+     * Support for collecting information on allocations in this class. Rprofmem/Rprof register a
+     * listener when active which, when memory profiling is enabled, is called with the object being
+     * allocated. Owing to the use of the Assumption, there should be no overhead when disabled.
+     */
+
+    private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>();
+    @CompilationFinal private static boolean enabled;
+    private static final CyclicAssumption noAllocationTracingAssumption = new CyclicAssumption("data allocation");
+
+    public static void setAllocationTracing(boolean newState) {
+        if (enabled != newState) {
+            noAllocationTracingAssumption.invalidate();
+            enabled = newState;
+        }
+    }
 
     private static <T> T traceDataCreated(T data) {
-        if (statsProfile.profile(stats != null)) {
-            stats.record(data);
+        if (enabled) {
+            for (Listener listener : listeners) {
+                listener.reportAllocation((RTypedValue) data);
+            }
         }
         return data;
     }
 
+    public interface Listener {
+        /**
+         * Invoked when an instance of an {@link RTypedValue} is created. Note that the initial
+         * state of the complex objects, i.e., those with additional {@code Object} subclass fields,
+         * which may also be {@link RTypedValue} instances is undefined other than by inspection. A
+         * listener that computes the "size" of an object must take into account that
+         * {@link RTypedValue} instances passed to a {@code createXXX} method will already have been
+         * reported, but other data such as {@code int[]} instances for array dimensions will not.
+         */
+        void reportAllocation(RTypedValue data);
+    }
+
+    /**
+     * Sets the listener of memory tracing events. For the time being there can only be one
+     * listener. This can be extended to an array should we need more listeners.
+     */
+    public static void addListener(Listener listener) {
+        listeners.addLast(listener);
+    }
+
+    /*
+     * (Legacy) support for R:PerfStats option. This does produce more information than Rprofmem
+     * regarding the types and length of the objects being allocated, but it does not record where
+     * in R the allocation took place.
+     */
     static {
         RPerfStats.register(new PerfHandler());
     }
 
-    private static class PerfHandler implements RPerfStats.Handler {
+    private static class PerfHandler implements RPerfStats.Handler, Listener {
         private static Map<Class<?>, RPerfStats.Histogram> histMap;
 
+        @Override
+        public void initialize(String optionData) {
+            histMap = new HashMap<>();
+            addListener(this);
+            setAllocationTracing(true);
+        }
+
+        @Override
+        public String getName() {
+            return "datafactory";
+        }
+
+        @Override
         @TruffleBoundary
-        void record(Object data) {
+        public void reportAllocation(RTypedValue data) {
             Class<?> klass = data.getClass();
             boolean isBounded = data instanceof RAbstractVector;
             RPerfStats.Histogram hist = histMap.get(klass);
@@ -527,17 +579,6 @@ public final class RDataFactory {
             hist.inc(length);
         }
 
-        @Override
-        public void initialize(String optionData) {
-            stats = this;
-            histMap = new HashMap<>();
-        }
-
-        @Override
-        public String getName() {
-            return "datafactory";
-        }
-
         @Override
         public void report() {
             RPerfStats.out().println("Scalar types");
@@ -558,5 +599,6 @@ public final class RDataFactory {
             }
             RPerfStats.out().println();
         }
+
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java
index 096356c36aaf5031ea8b85166e820cfbd7d960b5..2caf812292e1c4270b619fe81165d7e1066aa7b2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java
@@ -90,7 +90,7 @@ public final class RDouble extends RScalarVector implements RAbstractDoubleVecto
     @Override
     public RDoubleVector materialize() {
         RDoubleVector result = RDataFactory.createDoubleVectorFromScalar(getValue());
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java
index 075d9ee51126ff8bd297ebffb5b11bed48055b13..33ba1ebd83bac82664770babb137ec2908cf5148 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java
@@ -88,7 +88,7 @@ public final class RInteger extends RScalarVector implements RAbstractIntVector
     @Override
     public RIntVector materialize() {
         RIntVector result = RDataFactory.createIntVectorFromScalar(value);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java
index 05d0e6d1b1d642a39fc13b620d15a3727a3c130d..44e432804a569f815eeb52fe6b9533d3f79c97a4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java
@@ -94,7 +94,7 @@ public final class RLogical extends RScalarVector implements RAbstractLogicalVec
     @Override
     public RLogicalVector materialize() {
         RLogicalVector result = RDataFactory.createLogicalVectorFromScalar(value);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java
new file mode 100644
index 0000000000000000000000000000000000000000..14b70ab39912d6a656926780f59ffe37d48e3daa
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2016, 2016, 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.
+ *
+ * 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 com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.RCaller;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
+
+/**
+ * Support for the sizing of the objects that flow through the interpreter, i.e., mostly
+ * {@link RTypedValue}, but also including scalar types like {@code String} and dimension data for
+ * arrays, i.e., {@code int[]}.
+ *
+ * The actually implementation is controlled by {@link ObjectSizeFactory} to finesse problems with
+ * Java VMs that do not support reflection.
+ *
+ * Owing to the (implementation) complexity of some of the types, two levels of customization are
+ * provided:
+ * <ol>
+ * <li>A completely custom sizing implementation can be provided for a specific type. This effects
+ * all sizing computations.</li>
+ * <li>In any given call to {@link #getObjectSize} an instance of {@IgnoreObjectHandler} can passed.
+ * This allows some additional dynamic control over certain fields depending of the context of the
+ * call. For example, when tracking the incremental memory allocation via {@link RDataFactory}, we
+ * do not want to (double) count fields of type {@link RTypedValue}. However, when computing the
+ * total size of the object, e.g. for the {@code utils::object.size} builtin, we do want to count
+ * them.</li>
+ * </ol>
+ *
+ */
+public class RObjectSize {
+    public static final int INT_SIZE = 32;
+    public static final int DOUBLE_SIZE = 64;
+    public static final int BYTE_SIZE = 8;
+
+    public interface TypeCustomizer {
+        /**
+         * Allows complete control over sizing of a type registered with
+         * {@link #registerTypeCustomizer}.
+         */
+        long getObjectSize(Object obj);
+    }
+
+    public interface IgnoreObjectHandler {
+        /**
+         * Controls which fields of an object passed to {@link #getObjectSize} will be ignored.
+         * {@code rootObject} is the initiating object and {@code obj} is some field of that object
+         * or one of its components. The return value should be {@code true} if {@code obj} should
+         * be ignored.
+         */
+        boolean ignore(Object rootObject, Object obj);
+    }
+
+    /**
+     * Returns an estimate of the size of the this object, including the size of any object-valued
+     * fields, recursively. Evidently this is a snapshot and the size can change as, e.g.,
+     * attributes are added/removed.
+     *
+     * If called immediately after creation by {@link RDataFactory}, with an
+     * {@link IgnoreObjectHandler} that ignores objects created separately and, it provides an
+     * approximation of the incremental memory usage of the system.
+     *
+     * @param ignoreObjectHandler An object that is called to decide whether to include the
+     *            contribution a field of this object (and its sub-objects) in the result. Passing
+     *            {@code null} includes everything. N.B. {@code obj} is typed as {@code Object} only
+     *            to allow scalar typed such as {@code String} to be passed.
+     *
+     */
+    public static long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        return (int) ObjectSizeFactory.getInstance().getObjectSize(obj, ignoreObjectHandler);
+    }
+
+    /**
+     * Register a {@link TypeCustomizer} for {@code klass} and its subclasses. I.e. and object
+     * {@code obj} is customized iff {@code klass.isAssignableFrom(obj.getClass())}.
+     */
+    public static void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer) {
+        ObjectSizeFactory.getInstance().registerTypeCustomizer(klass, typeCustomizer);
+    }
+
+    /**
+     * This denotes a special customizer that completely ignores instances of the type and its
+     * subclasses. It allows a more efficient implementation as the type can be suppressed
+     * completely from the computation at the time fields of a containing type are analyzed.
+     */
+    public static final TypeCustomizer IGNORE = new TypeCustomizer() {
+
+        @Override
+        public long getObjectSize(Object obj) {
+            return 0;
+        }
+
+    };
+
+    // TODO construct proper customizers for some of these.
+    static {
+        registerTypeCustomizer(Frame.class, IGNORE);
+        registerTypeCustomizer(FrameDescriptor.class, IGNORE);
+        registerTypeCustomizer(Node.class, IGNORE);
+        registerTypeCustomizer(CallTarget.class, IGNORE);
+        registerTypeCustomizer(RBuiltinDescriptor.class, IGNORE);
+        registerTypeCustomizer(RPromise.Closure.class, IGNORE);
+        registerTypeCustomizer(Assumption.class, IGNORE);
+        registerTypeCustomizer(RCaller.class, IGNORE);
+        registerTypeCustomizer(SEXPTYPE.class, IGNORE);
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
index d843cacfb937a8518c91151e91e7e33b3c553076..a18f274d43736e0c7e9936de1eaa8d6adf0aa544 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
@@ -65,7 +65,7 @@ public final class RRaw extends RScalarVector implements RAbstractRawVector {
     @Override
     public RRawVector materialize() {
         RRawVector result = RDataFactory.createRawVector(new byte[]{value});
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java
index 6e4a7ca5d9590b16c937b8bb62608c6f25ef8cd7..e12aed17f8fbb58a7789401701ae49a288788850 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java
@@ -143,28 +143,28 @@ public abstract class RScalarVector extends RScalar implements RAbstractVector {
     @Override
     public RVector copyResized(int size, boolean fillNA) {
         RVector result = materialize().copyResized(size, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public RAbstractVector copyWithNewDimensions(int[] newDimensions) {
         RAbstractVector result = materialize().copyWithNewDimensions(newDimensions);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public RVector copyResizedWithDimensions(int[] newDimensions, boolean fillNA) {
         RVector result = materialize().copyResizedWithDimensions(newDimensions, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public RAbstractVector copyDropAttributes() {
         RVector result = materialize().copyDropAttributes();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
index 3dcc48a783d72896755c68f58238721596a0d86c..75a022269b5c8b5057b02859a0d6f750d412e76b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
@@ -81,7 +81,7 @@ public abstract class RSequence implements RAbstractVector {
 
     public final RVector createVector() {
         RVector result = internalCreateVector();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java
index 764f4c4b4ba2c88556989eb83461fdf4b8409bc5..af3281e0c19f749edcdbd5f3ab44ed4562315ce6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java
@@ -74,7 +74,7 @@ public final class RString extends RScalarVector implements RAbstractStringVecto
     @Override
     public RStringVector materialize() {
         RStringVector result = RDataFactory.createStringVector(new String[]{getValue()}, isComplete());
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
index 159addc3e228b3bda6e2e3bbcc9e9504544c5987..a75b54fdb4bd80a721e5dfb173f611368db7cb62 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
@@ -581,19 +581,19 @@ public abstract class RVector extends RSharingAttributeStorage implements RShare
 
     protected final RVector internalCopyAndReport() {
         RVector result = internalCopy();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     protected final RVector internalDeepCopyAndReport() {
         RVector result = internalDeepCopy();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     protected final RVector internalCopyResizedAndReport(int size, boolean fillNA) {
         RVector result = internalCopyResized(size, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
index 0fe0baccfc90e7e0bb0b1a374322b1d431b37cc2..99383ff1a2bf7e9f9c5afd5ed0158a00ffb02835 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.data.closures;
 
-import com.oracle.truffle.r.runtime.data.MemoryTracer;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RAttributes;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -129,14 +129,14 @@ abstract class RToVectorClosure implements RAbstractVector {
     @Override
     public final RAbstractVector copy() {
         RAbstractVector result = vector.copy();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public final RVector copyResized(int size, boolean fillNA) {
         RVector result = vector.copyResized(size, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
@@ -152,7 +152,7 @@ abstract class RToVectorClosure implements RAbstractVector {
     @Override
     public final RAbstractVector copyDropAttributes() {
         RAbstractVector result = vector.copyDropAttributes();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
index a2ae57288b474717f66033b2bacd43a882e5e899..a9b3eebf0606c05d5a434500e994a5ab907cc38c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
@@ -24,11 +24,11 @@ package com.oracle.truffle.r.runtime.data.model;
 
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.MemoryTracer;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.RVector;
 
 /**
- * When implementing, make sure to invoke related {@link MemoryTracer} methods.
+ * When implementing, make sure to invoke related {@link MemoryCopyTracer} methods.
  */
 public interface RAbstractVector extends RAbstractContainer {
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
index 09f5a10046d079cd9b7697e1d51e4fb172fb4556..0b601ce31970ad98c9347a687fc8de366dbcef6c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
@@ -68,6 +68,11 @@ public final class InstrumentationState implements RContext.ContextState {
      */
     private final RprofState rprofState;
 
+    /**
+     * The {@link RprofmemState} state, if any, associated with this {@link RContext}.
+     */
+    private final RprofmemState rprofmemState;
+
     private final TracememContext tracememContext;
 
     /**
@@ -96,32 +101,72 @@ public final class InstrumentationState implements RContext.ContextState {
      */
     private boolean debugGloballyDisabled;
 
+    private abstract static class RprofAdapter {
+        protected PrintWriter out;
+
+        /**
+         * Return current output or {@code null} if not profiling.
+         */
+        public PrintWriter out() {
+            return out;
+        }
+
+        public void setOut(PrintWriter out) {
+            this.out = out;
+        }
+    }
+
     /**
      * State used by {@code Rprof}.
      *
      */
-    public static final class RprofState {
-        private PrintWriter out;
+    public static final class RprofState extends RprofAdapter {
         private Thread profileThread;
         private ExecutionEventListener statementListener;
         private long intervalInMillis;
         private boolean lineProfiling;
+        private MemoryQuad memoryQuad;
+
+        public static final class MemoryQuad {
+            public long smallV;
+            public long largeV;
+            public long nodes;
+            public long copied;
+
+            public MemoryQuad copyAndClear() {
+                MemoryQuad result = new MemoryQuad();
+                result.copied = copied;
+                result.largeV = largeV;
+                result.smallV = smallV;
+                result.nodes = nodes;
+                copied = 0;
+                largeV = 0;
+                smallV = 0;
+                nodes = 0;
+                return result;
+            }
+        }
 
         public void initialize(PrintWriter outA, Thread profileThreadA, ExecutionEventListener statementListenerA, long intervalInMillisA,
-                        boolean lineProfilingA) {
+                        boolean lineProfilingA, boolean memoryProfilingA) {
             this.out = outA;
             this.profileThread = profileThreadA;
             this.statementListener = statementListenerA;
             this.intervalInMillis = intervalInMillisA;
             this.lineProfiling = lineProfilingA;
+            this.memoryQuad = memoryProfilingA ? new MemoryQuad() : null;
         }
 
         public boolean lineProfiling() {
             return lineProfiling;
         }
 
-        public PrintWriter out() {
-            return out;
+        public boolean memoryProfiling() {
+            return memoryQuad != null;
+        }
+
+        public MemoryQuad memoryQuad() {
+            return memoryQuad;
         }
 
         public long intervalInMillis() {
@@ -138,6 +183,28 @@ public final class InstrumentationState implements RContext.ContextState {
 
     }
 
+    public static final class RprofmemState extends RprofAdapter {
+        private double threshold;
+        private int pageCount;
+
+        public void initialize(PrintWriter outA, double thresholdA) {
+            this.out = outA;
+            this.threshold = thresholdA;
+        }
+
+        public double threshold() {
+            return threshold;
+        }
+
+        public int pageCount() {
+            return pageCount;
+        }
+
+        public void setPageCount(int pageCount) {
+            this.pageCount = pageCount;
+        }
+    }
+
     public static class BrowserState {
         private boolean inBrowser;
         private String lastEmptyLineCommand = "n";
@@ -162,6 +229,7 @@ public final class InstrumentationState implements RContext.ContextState {
     private InstrumentationState(Instrumenter instrumenter) {
         this.instrumenter = instrumenter;
         this.rprofState = new RprofState();
+        this.rprofmemState = new RprofmemState();
         this.tracememContext = new TracememContext();
         this.browserState = new BrowserState();
     }
@@ -215,6 +283,10 @@ public final class InstrumentationState implements RContext.ContextState {
         return rprofState;
     }
 
+    public RprofmemState getRprofmem() {
+        return rprofmemState;
+    }
+
     public TracememContext getTracemem() {
         return tracememContext;
     }