You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by GitBox <gi...@apache.org> on 2018/01/31 17:11:26 UTC

[GitHub] JaroslavTulach closed pull request #402: Profiler improvements

JaroslavTulach closed pull request #402: Profiler improvements
URL: https://github.com/apache/incubator-netbeans/pull/402
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/debugger.jpda.heapwalk/nbproject/project.xml b/debugger.jpda.heapwalk/nbproject/project.xml
index f2098057c..44eacf515 100644
--- a/debugger.jpda.heapwalk/nbproject/project.xml
+++ b/debugger.jpda.heapwalk/nbproject/project.xml
@@ -48,7 +48,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>1</release-version>
-                        <specification-version>1.103</specification-version>
+                        <specification-version>1.105</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
diff --git a/debugger.jpda.heapwalk/src/org/netbeans/modules/debugger/jpda/heapwalk/HeapImpl.java b/debugger.jpda.heapwalk/src/org/netbeans/modules/debugger/jpda/heapwalk/HeapImpl.java
index 91a19ce3b..5629340a7 100644
--- a/debugger.jpda.heapwalk/src/org/netbeans/modules/debugger/jpda/heapwalk/HeapImpl.java
+++ b/debugger.jpda.heapwalk/src/org/netbeans/modules/debugger/jpda/heapwalk/HeapImpl.java
@@ -160,6 +160,16 @@ public Iterator getAllInstancesIterator() {
         return Collections.emptyIterator();
     }
 
+    @Override
+    public boolean isRetainedSizeComputed() {
+        return true;
+    }
+
+    @Override
+    public boolean isRetainedSizeByClassComputed() {
+        return true;
+    }
+
     private static class InstancesIterator implements Iterator {
 
         private Iterator clsIt;
diff --git a/debugger.jpda.heapwalk/src/org/netbeans/modules/debugger/jpda/heapwalk/ObjectArrayInstanceImpl.java b/debugger.jpda.heapwalk/src/org/netbeans/modules/debugger/jpda/heapwalk/ObjectArrayInstanceImpl.java
index a549a014c..614924ff6 100644
--- a/debugger.jpda.heapwalk/src/org/netbeans/modules/debugger/jpda/heapwalk/ObjectArrayInstanceImpl.java
+++ b/debugger.jpda.heapwalk/src/org/netbeans/modules/debugger/jpda/heapwalk/ObjectArrayInstanceImpl.java
@@ -58,4 +58,9 @@ public int getLength() {
         return instances;
     }
 
+    @Override
+    public List getItems() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
 }
diff --git a/lib.profiler.common/manifest.mf b/lib.profiler.common/manifest.mf
index 4f7b5ef98..0e853e673 100644
--- a/lib.profiler.common/manifest.mf
+++ b/lib.profiler.common/manifest.mf
@@ -1,6 +1,6 @@
 Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.lib.profiler.common/1
 OpenIDE-Module-Localizing-Bundle: org/netbeans/lib/profiler/common/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.47
+OpenIDE-Module-Specification-Version: 1.48
 OpenIDE-Module-Needs: org.netbeans.lib.profiler.common.Profiler
 
diff --git a/lib.profiler.common/nbproject/project.xml b/lib.profiler.common/nbproject/project.xml
index 6ef6d5d59..969f6990b 100644
--- a/lib.profiler.common/nbproject/project.xml
+++ b/lib.profiler.common/nbproject/project.xml
@@ -45,6 +45,7 @@
             </module-dependencies>
             <friend-packages>
                 <friend>com.sun.tools.visualvm.core</friend>
+                <friend>com.sun.tools.visualvm.modules.appui</friend>
                 <friend>com.sun.tools.visualvm.profiler</friend>
                 <friend>com.sun.tools.visualvm.profiling</friend>
                 <friend>com.sun.tools.visualvm.sampler</friend>
diff --git a/lib.profiler.ui/manifest.mf b/lib.profiler.ui/manifest.mf
index c0a00988b..23c327c1e 100644
--- a/lib.profiler.ui/manifest.mf
+++ b/lib.profiler.ui/manifest.mf
@@ -1,5 +1,5 @@
 Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.lib.profiler.ui/1
 OpenIDE-Module-Localizing-Bundle: org/netbeans/lib/profiler/ui/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.137
+OpenIDE-Module-Specification-Version: 1.147
 
diff --git a/lib.profiler.ui/nbproject/project.xml b/lib.profiler.ui/nbproject/project.xml
index 1db2776b9..d3b8d70f7 100644
--- a/lib.profiler.ui/nbproject/project.xml
+++ b/lib.profiler.ui/nbproject/project.xml
@@ -31,7 +31,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>1</release-version>
-                        <specification-version>1.97</specification-version>
+                        <specification-version>1.106</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -82,7 +82,12 @@
             <friend-packages>
                 <friend>com.sun.tools.visualvm.application.views</friend>
                 <friend>com.sun.tools.visualvm.coredump</friend>
+                <friend>com.sun.tools.visualvm.heapviewer</friend>
+                <friend>com.sun.tools.visualvm.heapviewer.console</friend>
+                <friend>com.sun.tools.visualvm.heapviewer.truffle</friend>
                 <friend>com.sun.tools.visualvm.modules.appui</friend>
+                <friend>com.sun.tools.visualvm.profiler</friend>
+                <friend>com.sun.tools.visualvm.profiling</friend>
                 <friend>com.sun.tools.visualvm.sampler</friend>
                 <friend>com.sun.tools.visualvm.uisupport</friend>
                 <friend>oracle.jdevimpl.profiler.impl</friend>
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/AppearanceController.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/AppearanceController.java
new file mode 100644
index 000000000..5ba4fdd8e
--- /dev/null
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/AppearanceController.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.lib.profiler.ui;
+
+import javax.swing.JPanel;
+import org.openide.util.Lookup;
+
+/** Allows to control UI customizations by the NetBeans IDE, etc.
+ * 
+ * @since 1.147
+ */
+public class AppearanceController {
+    private static final AppearanceController DEFAULT;
+    static {
+        AppearanceController ac = Lookup.getDefault().lookup(AppearanceController.class);
+        DEFAULT = ac == null ? new AppearanceController() : ac;
+    }
+
+    public static AppearanceController getDefault() {
+        return DEFAULT;
+    }
+
+    public void customizeProfilerTableContainer(JPanel p) {
+    }
+
+    public void customizeLiveFlatProfilePanel(JPanel p) {
+    }
+
+    public void customizeThreadPanel(JPanel p) {
+    }
+
+    public boolean isAddToRootsVisible() {
+        return true;
+    }
+
+    public int[] invisibleLivenessResultsColumns() {
+        return new int[] { 7 };
+    }
+}
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/components/HTMLTextArea.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/components/HTMLTextArea.java
index f2abb7ada..98e6cb5bf 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/components/HTMLTextArea.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/components/HTMLTextArea.java
@@ -25,7 +25,6 @@
 import java.awt.datatransfer.StringSelection;
 import java.awt.datatransfer.Transferable;
 import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.awt.event.HierarchyEvent;
 import java.awt.event.HierarchyListener;
 import java.awt.event.InputEvent;
@@ -36,7 +35,7 @@
 import java.io.StringWriter;
 import java.io.Writer;
 import java.net.URL;
-import java.util.Hashtable;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.ResourceBundle;
 import javax.swing.*;
@@ -44,6 +43,7 @@
 import javax.swing.event.HyperlinkListener;
 import javax.swing.text.AttributeSet;
 import javax.swing.text.BadLocationException;
+import javax.swing.text.EditorKit;
 import javax.swing.text.Element;
 import javax.swing.text.StyleConstants;
 import javax.swing.text.html.HTML;
@@ -210,7 +210,7 @@ public static String decode(String str) {
 
         private static synchronized Map getEntities() {
             if (entities == null) {
-                entities = new Hashtable();
+                entities = new HashMap();
                 //Quotation mark
                 entities.put("quot", "\""); //NOI18N
                                             //Ampersand
@@ -530,15 +530,8 @@ private static synchronized Map getEntities() {
 
     //~ Instance fields ----------------------------------------------------------------------------------------------------------
 
-    private ActionListener popupListener;
-    private JMenuItem itemCopy;
-    private JMenuItem itemCut;
-    private JMenuItem itemDelete;
-    private JMenuItem itemPaste;
-    private JMenuItem itemSelectAll;
+    private URL activeLink;
 
-    // --- Popup menu support ----------------------------------------------------
-    private JPopupMenu popupMenu;
     private boolean showPopup = true;
 
     // --- Lazy setting text ---------------------------------------------------
@@ -549,7 +542,7 @@ private static synchronized Map getEntities() {
     //~ Constructors -------------------------------------------------------------------------------------------------------------
 
     public HTMLTextArea() {
-        setEditorKit(new HTMLEditorKit());
+        setContentType("text/html"); // NOI18N
         setEditable(false);
         setOpaque(true);
         setAutoscrolls(true);
@@ -582,6 +575,11 @@ public HTMLTextArea(String text) {
 
     //~ Methods ------------------------------------------------------------------------------------------------------------------
     
+    public EditorKit getEditorKitForContentType(String type) {
+        // Always assumes "text/html" as this is a HTML displayer
+        return new HTMLEditorKit();
+    }
+    
     public void setOpaque(boolean o) {
         super.setOpaque(o);
         if (UIUtils.isNimbusLookAndFeel() && !o)
@@ -685,14 +683,21 @@ public void hyperlinkUpdate(HyperlinkEvent e) {
         }
 
         if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
-            showURL(e.getURL());
+            activeLink = e.getURL();
+            showURL(activeLink, e.getInputEvent());
         } else if (e.getEventType() == HyperlinkEvent.EventType.ENTERED) {
+            activeLink = e.getURL();
             setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
         } else if (e.getEventType() == HyperlinkEvent.EventType.EXITED) {
+            activeLink = null;
             setCursor(Cursor.getDefaultCursor());
         }
     }
     
+    public URL getActiveLink() {
+        return activeLink;
+    }
+    
     protected void processMouseEvent(MouseEvent e) {
         if (e.isPopupTrigger()) showPopupMenu(e);
         super.processMouseEvent(e);
@@ -711,10 +716,10 @@ protected void processKeyEvent(KeyEvent e) {
     
     private void showPopupMenu(MouseEvent e) {
         if (isEnabled() && isFocusable() && showPopup) {
-            JPopupMenu popup = getPopupMenu();
+            JPopupMenu popup = new JPopupMenu();
+            populatePopup(popup);
 
-            if (popup != null) {
-                updatePopupMenu();
+            if (popup.getComponentCount() > 0) {
 
                 if (!hasFocus()) requestFocus(); // required for Select All functionality
                 
@@ -744,39 +749,64 @@ private void showPopupMenu(MouseEvent e) {
         }
     }
 
-    protected JPopupMenu getPopupMenu() {
-        if (popupMenu == null) {
-            popupMenu = createPopupMenu();
-        }
-
-        return popupMenu;
-    }
-
-    protected JPopupMenu createPopupMenu() {
-        JPopupMenu popup = new JPopupMenu();
-
-        popupListener = createPopupListener();
-
-        itemCut = new JMenuItem(CUT_STRING);
-        itemCopy = new JMenuItem(COPY_STRING);
-        itemPaste = new JMenuItem(PASTE_STRING);
-        itemDelete = new JMenuItem(DELETE_STRING);
-        itemSelectAll = new JMenuItem(SELECT_ALL_STRING);
-
-        itemCut.addActionListener(popupListener);
-        itemCopy.addActionListener(popupListener);
-        itemPaste.addActionListener(popupListener);
-        itemDelete.addActionListener(popupListener);
-        itemSelectAll.addActionListener(popupListener);
-
-        popup.add(itemCut);
-        popup.add(itemCopy);
-        popup.add(itemPaste);
-        popup.add(itemDelete);
+    protected void populatePopup(JPopupMenu popup) {
+        popup.add(createCutMenuItem());
+        popup.add(createCopyMenuItem());
+        popup.add(createPasteMenuItem());
+        popup.add(createDeleteMenuItem());
         popup.addSeparator();
-        popup.add(itemSelectAll);
-
-        return popup;
+        popup.add(createSelectAllMenuItem());
+    }
+    
+    protected JMenuItem createCutMenuItem() {
+        return new JMenuItem(CUT_STRING) {
+            { setEnabled(isEditable() && getSelectedText() != null); }
+            protected void fireActionPerformed(ActionEvent e) { cut(); }
+        };
+    }
+    
+    protected JMenuItem createCopyMenuItem() {
+        return new JMenuItem(COPY_STRING) {
+            { setEnabled(getSelectedText() != null); }
+            protected void fireActionPerformed(ActionEvent e) { copy(); }
+        };
+    }
+    
+    protected JMenuItem createPasteMenuItem() {
+        return new JMenuItem(PASTE_STRING) {
+            {
+                if (isEditable()) {
+                    try {
+                        Transferable clipboardContent = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(this);
+                        setEnabled(clipboardContent != null && clipboardContent.isDataFlavorSupported(DataFlavor.stringFlavor));
+                    } catch (Exception e) {
+                        setEnabled(false);
+                    }
+                } else {
+                    setEnabled(false);
+                }
+            }
+            protected void fireActionPerformed(ActionEvent e) { paste(); }
+        };
+    }
+    
+    protected JMenuItem createDeleteMenuItem() {
+        return new JMenuItem(DELETE_STRING) {
+            {
+                if (isEditable()) {
+                    setEnabled(getSelectedText() != null);
+                } else {
+                    setVisible(false);
+                }
+            }
+            protected void fireActionPerformed(ActionEvent e) { deleteSelection(); }
+        };
+    }
+    
+    protected JMenuItem createSelectAllMenuItem() {
+        return new JMenuItem(SELECT_ALL_STRING) {
+            protected void fireActionPerformed(ActionEvent e) { selectAll(); }
+        };
     }
     
     public void paste() {
@@ -785,54 +815,13 @@ public void paste() {
                                     .getTransferData(DataFlavor.stringFlavor).toString());
         } catch (Exception ex) {}
     }
+    
+    protected void showURL(URL url, InputEvent e) {
+        showURL(url);
+    }
 
     protected void showURL(URL url) {
         // override to react to URL clicks
     }
-
-    protected void updatePopupMenu() {
-        // Cut
-        itemCut.setEnabled(isEditable() && (getSelectedText() != null));
-
-        // Copy
-        itemCopy.setEnabled(getSelectedText() != null);
-
-        // Paste
-        try {
-            Transferable clipboardContent = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(this);
-            itemPaste.setEnabled(isEditable() && (clipboardContent != null)
-                                 && clipboardContent.isDataFlavorSupported(DataFlavor.stringFlavor));
-        } catch (Exception e) {
-            itemPaste.setEnabled(false);
-        }
-
-        // Delete
-        if (isEditable()) {
-            itemDelete.setVisible(true);
-            itemDelete.setEnabled(getSelectedText() != null);
-        } else {
-            itemDelete.setVisible(false);
-        }
-
-        // Select All
-        // always visible and enabled...
-    }
-
-    private ActionListener createPopupListener() {
-        return new ActionListener() {
-                public void actionPerformed(ActionEvent e) {
-                    if (e.getSource() == itemCut) {
-                        cut();
-                    } else if (e.getSource() == itemCopy) {
-                        copy();
-                    } else if (e.getSource() == itemPaste) {
-                        paste();
-                    } else if (e.getSource() == itemDelete) {
-                        deleteSelection();
-                    } else if (e.getSource() == itemSelectAll) {
-                        selectAll();
-                    }
-                }
-            };
-    }
+    
 }
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/components/ProfilerToolbar.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/components/ProfilerToolbar.java
index 86acf0b1f..3d81de9c3 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/components/ProfilerToolbar.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/components/ProfilerToolbar.java
@@ -142,6 +142,7 @@ public void remove(ProfilerToolbar toolbar) {
         @Override
         public Component add(Action action) {
             Component c = toolbar.add(action);
+            tweakComponent(c);
             toolbar.repaint();
             return c;
         }
@@ -149,6 +150,7 @@ public Component add(Action action) {
         @Override
         public Component add(Component component) {
             Component c = toolbar.add(component);
+            tweakComponent(c);
             toolbar.repaint();
             return c;
         }
@@ -156,6 +158,7 @@ public Component add(Component component) {
         @Override
         public Component add(Component component, int index) {
             Component c = toolbar.add(component, index);
+            tweakComponent(c);
             toolbar.repaint();
             return c;
         }
@@ -197,6 +200,11 @@ public int getComponentCount() {
             return toolbar.getComponentCount();
         }
         
+        
+        private void tweakComponent(Component c) {
+            if (c instanceof JComponent) ((JComponent)c).setOpaque(false);
+        }
+        
     }
     
     
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/CCTDisplay.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/CCTDisplay.java
index 7321adfcb..997b508c5 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/CCTDisplay.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/CCTDisplay.java
@@ -837,7 +837,7 @@ protected void enableDisablePopup(PrestimeCPUCCTNode node) {
         if (popupShowSource != null) popupShowSource.setEnabled(regularNode && isShowSourceAvailable());
         if (popupShowSubtree != null) popupShowSubtree.setEnabled(regularNode);
         if (popupShowReverse != null) popupShowReverse.setEnabled(regularNode);
-        popupAddToRoots.setEnabled(regularNode && isAddToRootsAvailable());
+        if (popupAddToRoots != null) popupAddToRoots.setEnabled(regularNode && isAddToRootsAvailable());
         popupFind.setEnabled(regularNode);
         // Allow the selection handler to change state of popupFind
         if (selectionHandler != null) selectionHandler.methodSelected(node.getThreadId(), node.getMethodId(), currentView);
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/CPUResultsPanel.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/CPUResultsPanel.java
index d4adef6d0..8f2758ed8 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/CPUResultsPanel.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/CPUResultsPanel.java
@@ -31,6 +31,7 @@
 import javax.swing.*;
 import javax.swing.table.TableCellRenderer;
 import javax.swing.tree.TreePath;
+import org.netbeans.lib.profiler.ui.AppearanceController;
 import org.netbeans.modules.profiler.api.GoToSource;
 
 
@@ -124,7 +125,9 @@ public void changeView(int view) {
         currentView = view;
 
         if (popupShowSource != null) popupShowSource.setEnabled(isShowSourceAvailable());
-        popupAddToRoots.setEnabled(isAddToRootsAvailable());
+        if (popupAddToRoots != null) {
+            popupAddToRoots.setEnabled(isAddToRootsAvailable());
+        }
 
         actionsHandler.viewChanged(view); // notify the actions handler about this
     }
@@ -153,7 +156,9 @@ protected boolean isShowSourceAvailable() {
     protected JPopupMenu createPopupMenu() {
         JPopupMenu popup = new JPopupMenu();
         if (GoToSource.isAvailable()) popupShowSource = new JMenuItem();
-        popupAddToRoots = new JMenuItem();
+        if (AppearanceController.getDefault().isAddToRootsVisible()) {
+            popupAddToRoots = new JMenuItem();
+        }
         popupFind = new JMenuItem();
 
         Font boldfont = popup.getFont().deriveFont(Font.BOLD);
@@ -198,13 +203,15 @@ public void actionPerformed(ActionEvent evt) {
 
         popup.add(popupFind);
 
-        popup.addSeparator();
-
-        popupAddToRoots.setText(ROOT_METHODS_ITEM_NAME);
-        popup.add(popupAddToRoots);
+        if (popupAddToRoots != null) {
+            popup.addSeparator();
 
+            popupAddToRoots.setText(ROOT_METHODS_ITEM_NAME);
+            popup.add(popupAddToRoots);
+            popupAddToRoots.addActionListener(menuListener);
+        }
+        
         if (popupShowSource != null) popupShowSource.addActionListener(menuListener);
-        popupAddToRoots.addActionListener(menuListener);
         popupFind.addActionListener(menuListener);
 
         return popup;
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/FlatProfilePanel.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/FlatProfilePanel.java
index cb5ddb081..acba3cd81 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/FlatProfilePanel.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/FlatProfilePanel.java
@@ -840,7 +840,7 @@ public void keyPressed(KeyEvent e) {
 
                             popupPath = null;
                             methodId = flatProfileContainer.getMethodIdAtRow(selectedRow);
-                            popupAddToRoots.setVisible(true);
+                            if (popupAddToRoots != null) popupAddToRoots.setVisible(true);
 
                             Rectangle cellRect = resTable.getCellRect(selectedRow, 0, false);
 
@@ -870,8 +870,7 @@ public void mouseClicked(MouseEvent e) {
                     if (line == -1) {
                         if (popupShowSource != null) popupShowSource.setVisible(false);
                         if (popupShowReverse != null) popupShowReverse.setVisible(false);
-
-                        popupAddToRoots.setVisible(false);
+                        if (popupAddToRoots != null) popupAddToRoots.setVisible(false);
 
                         if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
                             popupPath = null;
@@ -882,8 +881,8 @@ public void mouseClicked(MouseEvent e) {
                         
                         if (popupShowSource != null) popupShowSource.setVisible(true);
                         if (popupShowReverse != null) popupShowReverse.setVisible(true);
-
-                        popupAddToRoots.setVisible(true);
+                        if (popupAddToRoots != null) popupAddToRoots.setVisible(true);
+                        
                         methodId = flatProfileContainer.getMethodIdAtRow(line);
 
                         if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveCPUView.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveCPUView.java
index 6016fb9bc..6af2c9c06 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveCPUView.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveCPUView.java
@@ -28,8 +28,6 @@
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 import javax.swing.AbstractAction;
 import javax.swing.ActionMap;
 import javax.swing.BorderFactory;
@@ -43,37 +41,26 @@
 import javax.swing.plaf.basic.BasicSplitPaneDivider;
 import javax.swing.plaf.basic.BasicSplitPaneUI;
 import javax.swing.tree.TreeNode;
-import org.netbeans.lib.profiler.ProfilerClient;
 import org.netbeans.lib.profiler.client.ClientUtils;
 import org.netbeans.lib.profiler.results.CCTNode;
-import org.netbeans.lib.profiler.results.RuntimeCCTNode;
-import org.netbeans.lib.profiler.results.cpu.CPUCCTProvider;
 import org.netbeans.lib.profiler.results.cpu.CPUResultsDiff;
 import org.netbeans.lib.profiler.results.cpu.CPUResultsSnapshot;
 import org.netbeans.lib.profiler.results.cpu.FlatProfileContainer;
 import org.netbeans.lib.profiler.results.cpu.PrestimeCPUCCTNode;
 import org.netbeans.lib.profiler.ui.UIUtils;
 import org.netbeans.lib.profiler.ui.components.JExtendedSplitPane;
-import org.netbeans.lib.profiler.ui.memory.LiveMemoryView;
 import org.netbeans.lib.profiler.ui.results.DataView;
 import org.netbeans.lib.profiler.ui.swing.FilterUtils;
 import org.netbeans.lib.profiler.ui.swing.ProfilerTable;
 import org.netbeans.lib.profiler.ui.swing.ProfilerTreeTable;
 import org.netbeans.lib.profiler.ui.swing.SearchUtils;
 import org.netbeans.lib.profiler.utils.Wildcards;
-import org.openide.util.Lookup;
-import org.openide.util.lookup.ServiceProvider;
 
 /**
  *
  * @author Jiri Sedlacek
  */
 public abstract class LiveCPUView extends JPanel {
-
-    private static final int MIN_UPDATE_DIFF = 900;
-    private static final int MAX_UPDATE_DIFF = 1400;
-    
-    private final ResultsMonitor rm;
     
     private CPUResultsSnapshot snapshot;
     private CPUResultsSnapshot refSnapshot;
@@ -89,43 +76,14 @@
     private ThreadsSelector threadsSelector;
     
     private long lastupdate;
-    private volatile boolean paused;
-    private volatile boolean forceRefresh;
     private volatile boolean refreshIsRunning;
     
     private ExecutorService executor;
     
     
-    @ServiceProvider(service=CPUCCTProvider.Listener.class)
-    public static final class ResultsMonitor implements CPUCCTProvider.Listener {
-
-        private LiveCPUView view;
-        
-        @Override
-        public void cctEstablished(RuntimeCCTNode appRootNode, boolean empty) {
-            if (view != null && !empty) {
-                try {
-                    view.refreshData(appRootNode);
-                } catch (ClientUtils.TargetAppOrVMTerminated ex) {
-                    Logger.getLogger(LiveMemoryView.class.getName()).log(Level.FINE, null, ex);
-                }
-            }
-        }
-
-        @Override
-        public void cctReset() {
-            if (view != null) {
-                view.resetData();
-            }
-        }
-    }
-    
     public LiveCPUView(Set<ClientUtils.SourceCodeSelection> selection) {
         initUI(selection);
         registerActions();
-        
-        rm = Lookup.getDefault().lookup(ResultsMonitor.class);
-        rm.view = this;
     }
     
     
@@ -152,79 +110,58 @@ void reset() {
         return threadsSelector;
     }
     
-    public void setPaused(boolean paused) {
-        this.paused = paused;
+    public boolean isRefreshRunning() {
+        return refreshIsRunning;
     }
-
-    public void setForceRefresh(boolean forceRefresh) {
-        this.forceRefresh = forceRefresh;
+    
+    public long getLastUpdate() {
+        return lastupdate;
     }
 
-    private void refreshData(RuntimeCCTNode appRootNode) throws ClientUtils.TargetAppOrVMTerminated {
-        if ((lastupdate + MIN_UPDATE_DIFF > System.currentTimeMillis() || paused) && !forceRefresh) return;
+    public void setData(final CPUResultsSnapshot snapshotData, final boolean sampledData) {
         if (refreshIsRunning) return;
         refreshIsRunning = true;
-        try {
-            ProfilerClient client = getProfilerClient();
-            final CPUResultsSnapshot snapshotData =
-                    client.getStatus().getInstrMethodClasses() == null ?
-                    null : client.getCPUProfilingResultsSnapshot(false);
-            final boolean _sampled = snapshotData == null ? true :
-                                     client.getCurrentInstrType() == ProfilerClient.INSTR_NONE_SAMPLING;
-            UIUtils.runInEventDispatchThread(new Runnable() {
-                public void run() {
-                    snapshot = snapshotData;
-                    sampled = _sampled;
-                    setData();
-                    lastupdate = System.currentTimeMillis();
-                    forceRefresh = false;
-                }
-            });
-        } catch (CPUResultsSnapshot.NoDataAvailableException e) {
-            refreshIsRunning = false;
-        } catch (Throwable t) {
-            refreshIsRunning = false;
-            if (t instanceof ClientUtils.TargetAppOrVMTerminated) {
-                throw ((ClientUtils.TargetAppOrVMTerminated)t);
-            } else {
-                Logger.getLogger(LiveCPUView.class.getName()).log(Level.SEVERE, null, t);
+        
+        UIUtils.runInEventDispatchThread(new Runnable() {
+            public void run() {
+                snapshot = snapshotData;
+                sampled = sampledData;
+
+                setData();
             }
-        }
+        });
     }
     
     private void setData() {
-        UIUtils.runInEventDispatchThread(new Runnable() {
-            public void run() {
-                if (snapshot == null) {
-                    resetData();
-                    refreshIsRunning = false;
-                } else {
-                    getExecutor().submit(new Runnable() {
-                        public void run() {
-                            final CPUResultsSnapshot _snapshot = refSnapshot == null ? snapshot :
-                                                                 refSnapshot.createDiff(snapshot);
+        if (snapshot == null) {
+            resetData();
+            refreshIsRunning = false;
+        } else {
+            getExecutor().submit(new Runnable() {
+                public void run() {
+                    final CPUResultsSnapshot _snapshot = refSnapshot == null ? snapshot :
+                                                         refSnapshot.createDiff(snapshot);
 
-                            final FlatProfileContainer flatData = _snapshot.getFlatProfile(selectedThreads, CPUResultsSnapshot.METHOD_LEVEL_VIEW);
-            
-                            final Map<Integer, ClientUtils.SourceCodeSelection> idMap = _snapshot.getMethodIDMap(CPUResultsSnapshot.METHOD_LEVEL_VIEW);
+                    final FlatProfileContainer flatData = _snapshot.getFlatProfile(selectedThreads, CPUResultsSnapshot.METHOD_LEVEL_VIEW);
+
+                    final Map<Integer, ClientUtils.SourceCodeSelection> idMap = _snapshot.getMethodIDMap(CPUResultsSnapshot.METHOD_LEVEL_VIEW);
 
-                            SwingUtilities.invokeLater(new Runnable() {
-                                public void run() {
-                                    try {
-                                        boolean diff = _snapshot instanceof CPUResultsDiff;
-                                        forwardCallsView.setData(_snapshot, idMap, CPUResultsSnapshot.METHOD_LEVEL_VIEW, selectedThreads, mergedThreads, sampled, diff);
-                                        hotSpotsView.setData(flatData, idMap, sampled, diff);
-                                        reverseCallsView.setData(_snapshot, idMap, CPUResultsSnapshot.METHOD_LEVEL_VIEW, selectedThreads, mergedThreads, sampled, diff);
-                                    } finally {
-                                        refreshIsRunning = false;
-                                    }
-                                }
-                            });
+                    SwingUtilities.invokeLater(new Runnable() {
+                        public void run() {
+                            try {
+                                boolean diff = _snapshot instanceof CPUResultsDiff;
+                                forwardCallsView.setData(_snapshot, idMap, CPUResultsSnapshot.METHOD_LEVEL_VIEW, selectedThreads, mergedThreads, sampled, diff);
+                                hotSpotsView.setData(flatData, idMap, sampled, diff);
+                                reverseCallsView.setData(_snapshot, idMap, CPUResultsSnapshot.METHOD_LEVEL_VIEW, selectedThreads, mergedThreads, sampled, diff);
+                            } finally {
+                                refreshIsRunning = false;
+                                lastupdate = System.currentTimeMillis();
+                            }
                         }
                     });
                 }
-            }
-        });
+            });
+        }
     }
     
     public boolean setDiffView(final boolean diff) {
@@ -238,12 +175,6 @@ public void run() {
         return true;
     }
     
-    public void refreshData() throws ClientUtils.TargetAppOrVMTerminated {
-        if ((lastupdate + MAX_UPDATE_DIFF < System.currentTimeMillis() && !paused) || forceRefresh) {
-            getProfilerClient().forceObtainedResultsDump(true);
-        }        
-    }
-    
     public void resetData() {
         UIUtils.runInEventDispatchThread(new Runnable() {
             public void run() {
@@ -272,16 +203,10 @@ public void refreshSelection() {
     }
     
     
-    public void cleanup() {
-        if (rm.view == this) rm.view = null;
-    }
-    
-    
-    protected abstract ProfilerClient getProfilerClient();
-    
-    
     protected boolean profileMethodSupported() { return true; }
     
+    protected boolean profileClassSupported() { return true; }
+    
     
     protected abstract boolean showSourceSupported();
     
@@ -446,17 +371,26 @@ private void populatePopup(final DataView invoker, JPopupMenu popup, final Objec
             popup.addSeparator();
         }
         
-        popup.add(new JMenuItem(CPUView.ACTION_PROFILE_METHOD) {
-            { setEnabled(profileMethodSupported() && userValue != null && CPUTableView.isSelectable(userValue)); }
+        if (profileMethodSupported()) popup.add(new JMenuItem(CPUView.ACTION_PROFILE_METHOD) {
+            { setEnabled(userValue != null && CPUTableView.isSelectable(userValue)); }
             protected void fireActionPerformed(ActionEvent e) { profileMethod(userValue); }
         });
         
-        popup.add(new JMenuItem(CPUView.ACTION_PROFILE_CLASS) {
+        if (profileClassSupported()) popup.add(new JMenuItem(CPUView.ACTION_PROFILE_CLASS) {
             { setEnabled(userValue != null); }
             protected void fireActionPerformed(ActionEvent e) { profileClass(userValue); }
         });
         
-        popup.addSeparator();
+        if (profileMethodSupported() || profileClassSupported()) popup.addSeparator();
+        
+        JMenuItem[] customItems = invoker.createCustomMenuItems(this, value, userValue);
+        if (customItems != null) {
+            for (JMenuItem customItem : customItems) popup.add(customItem);
+            popup.addSeparator();
+        }
+        
+        customizeNodePopup(invoker, popup, value, userValue);
+        
         if (invoker == forwardCallsView) {
             final ProfilerTreeTable ttable = (ProfilerTreeTable)forwardCallsView.getResultsComponent();
             int column = ttable.convertColumnIndexToView(ttable.getMainColumn());
@@ -686,6 +620,8 @@ public int getNodeType(TreeNode tnode) {
         };
     }
     
+    protected void customizeNodePopup(DataView invoker, JPopupMenu popup, Object value, ClientUtils.SourceCodeSelection userValue) {}
+    
     
     private synchronized ExecutorService getExecutor() {
         if (executor == null) executor = Executors.newSingleThreadExecutor();
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveCPUViewUpdater.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveCPUViewUpdater.java
new file mode 100644
index 000000000..758c8310c
--- /dev/null
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveCPUViewUpdater.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.lib.profiler.ui.cpu;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.lib.profiler.ProfilerClient;
+import org.netbeans.lib.profiler.client.ClientUtils;
+import org.netbeans.lib.profiler.results.RuntimeCCTNode;
+import org.netbeans.lib.profiler.results.cpu.CPUCCTProvider;
+import org.netbeans.lib.profiler.results.cpu.CPUResultsSnapshot;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Jiri Sedlacek
+ */
+public class LiveCPUViewUpdater {
+    
+    private static final int MIN_UPDATE_DIFF = 900;
+    private static final int MAX_UPDATE_DIFF = 1400;
+    
+    
+    private CCTHandler handler;
+    
+    private final LiveCPUView cpuView;
+    private final ProfilerClient client;
+    
+    private volatile boolean paused;
+    private volatile boolean forceRefresh;
+    
+    
+    
+    public LiveCPUViewUpdater(LiveCPUView cpuView, ProfilerClient client) {
+        this.cpuView = cpuView;
+        this.client = client;
+        
+        handler = CCTHandler.registerUpdater(this);
+    }
+    
+    
+    
+    public void setPaused(boolean paused) {
+        this.paused = paused;
+    }
+
+    public void setForceRefresh(boolean forceRefresh) {
+        this.forceRefresh = forceRefresh;
+    }
+    
+    public void update() throws ClientUtils.TargetAppOrVMTerminated {
+        if (forceRefresh || (!paused && cpuView.getLastUpdate() + MAX_UPDATE_DIFF < System.currentTimeMillis()))
+            client.forceObtainedResultsDump(true);
+    }
+    
+    public void cleanup() {
+        handler.unregisterUpdater(this);
+        handler = null;
+    }
+    
+    
+    private void updateData() throws ClientUtils.TargetAppOrVMTerminated, CPUResultsSnapshot.NoDataAvailableException {
+        if (!forceRefresh && (paused || cpuView.getLastUpdate() + MIN_UPDATE_DIFF > System.currentTimeMillis())) return;
+        
+        boolean sampling = client.getCurrentInstrType() == ProfilerClient.INSTR_NONE_SAMPLING;
+        CPUResultsSnapshot data = client.getStatus().getInstrMethodClasses() == null ?
+                           null : client.getCPUProfilingResultsSnapshot(false);
+        cpuView.setData(data, sampling);
+        
+        forceRefresh = false;
+    }
+    
+    private void resetData() {
+        cpuView.resetData();
+    }
+    
+    
+    @ServiceProvider(service=CPUCCTProvider.Listener.class)
+    public static class CCTHandler implements CPUCCTProvider.Listener {
+        
+        private final List<LiveCPUViewUpdater> updaters = new ArrayList();
+        
+        
+        public static CCTHandler registerUpdater(LiveCPUViewUpdater updater) {
+            CCTHandler handler = Lookup.getDefault().lookup(CCTHandler.class);
+            handler.updaters.add(updater);
+            return handler;
+        }
+        
+        public void unregisterUpdater(LiveCPUViewUpdater updater) {
+            updaters.remove(updater);
+        }
+        
+
+        public final void cctEstablished(RuntimeCCTNode appRootNode, boolean empty) {
+            if (!empty) {
+                for (LiveCPUViewUpdater updater : updaters) try {
+                    updater.updateData();
+                } catch (ClientUtils.TargetAppOrVMTerminated ex) {
+                } catch (CPUResultsSnapshot.NoDataAvailableException ex) {
+                    Logger.getLogger(LiveCPUView.class.getName()).log(Level.FINE, null, ex);
+                }
+            }
+        }
+
+        public final void cctReset() {
+            for (LiveCPUViewUpdater updater : updaters) updater.resetData();
+        }
+
+    }
+    
+}
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveFlatProfilePanel.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveFlatProfilePanel.java
index d5d9157fa..47650a7d2 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveFlatProfilePanel.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/LiveFlatProfilePanel.java
@@ -33,6 +33,7 @@
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import org.netbeans.lib.profiler.results.ExportDataDumper;
+import org.netbeans.lib.profiler.ui.AppearanceController;
 
 
 /**
@@ -158,6 +159,7 @@ public void exportData(int exportedFileType, ExportDataDumper eDD, String viewNa
 
     private void initComponents() {
         setLayout(new BorderLayout());
+        AppearanceController.getDefault().customizeLiveFlatProfilePanel(this);
 
         noResultsPanel = new JPanel();
         noResultsPanel.setLayout(new BorderLayout());
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/ReverseCallGraphPanel.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/ReverseCallGraphPanel.java
index 894ea6284..26af9855e 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/ReverseCallGraphPanel.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/ReverseCallGraphPanel.java
@@ -53,6 +53,7 @@
 import javax.swing.table.TableColumnModel;
 import org.netbeans.lib.profiler.results.FilterSortSupport;
 import org.netbeans.lib.profiler.results.cpu.PrestimeCPUCCTNodeFree;
+import org.netbeans.lib.profiler.ui.AppearanceController;
 import org.netbeans.lib.profiler.ui.components.FilterComponent;
 import org.netbeans.modules.profiler.api.GoToSource;
 import org.netbeans.modules.profiler.api.icons.Icons;
@@ -592,7 +593,7 @@ public void stateChanged(ChangeEvent e) {
     private void enableDisablePopup(PrestimeCPUCCTNode node) {
         boolean regularNode = node.getMethodId() != 0 && !node.isFiltered();
         if (popupShowSource != null) popupShowSource.setEnabled(regularNode && isShowSourceAvailable());
-        popupAddToRoots.setEnabled(regularNode && isAddToRootsAvailable());
+        if (popupAddToRoots != null) popupAddToRoots.setEnabled(regularNode && isAddToRootsAvailable());
     }
 
     public void requestFocus() {
@@ -619,7 +620,9 @@ public void reset() {
     protected JPopupMenu createPopupMenu() {
         JPopupMenu popup = new JPopupMenu();
         if (GoToSource.isAvailable()) popupShowSource = new JMenuItem();
-        popupAddToRoots = new JMenuItem();
+        if (AppearanceController.getDefault().isAddToRootsVisible()) {
+            popupAddToRoots = new JMenuItem();
+        }
 
         Font boldfont = popup.getFont().deriveFont(Font.BOLD);
 
@@ -630,9 +633,10 @@ protected JPopupMenu createPopupMenu() {
             popup.addSeparator();
         }
 
-
-        popupAddToRoots.setText(ADD_ROOT_METHOD_POPUP_ITEM);
-        popup.add(popupAddToRoots);
+        if (AppearanceController.getDefault().isAddToRootsVisible()) {
+            popupAddToRoots.setText(ADD_ROOT_METHOD_POPUP_ITEM);
+            popup.add(popupAddToRoots);
+        }
 
         ActionListener menuListener = new ActionListener() {
             public void actionPerformed(ActionEvent evt) {
@@ -641,7 +645,7 @@ public void actionPerformed(ActionEvent evt) {
         };
 
         if (popupShowSource != null) popupShowSource.addActionListener(menuListener);
-        popupAddToRoots.addActionListener(menuListener);
+        if (popupAddToRoots != null) popupAddToRoots.addActionListener(menuListener);
 
         return popup;
     }
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/SnapshotCPUView.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/SnapshotCPUView.java
index bd92d2709..26e218fd9 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/SnapshotCPUView.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/SnapshotCPUView.java
@@ -129,8 +129,12 @@ public void setRefSnapshot(CPUResultsSnapshot snapshot) {
     }
     
     
+    protected boolean profileMethodEnabled() { return true; }
+    
     protected boolean profileMethodSupported() { return true; }
     
+    protected boolean profileClassSupported() { return true; }
+    
     
     protected abstract boolean showSourceSupported();
     
@@ -445,19 +449,26 @@ private void populatePopup(final DataView invoker, JPopupMenu popup, final Objec
             popup.addSeparator();
         }
         
-        popup.add(new JMenuItem(CPUView.ACTION_PROFILE_METHOD) {
-            { setEnabled(profileMethodSupported() && userValue != null && aggregation == CPUResultsSnapshot.METHOD_LEVEL_VIEW && CPUTableView.isSelectable(userValue)); }
+        if (profileMethodSupported()) popup.add(new JMenuItem(CPUView.ACTION_PROFILE_METHOD) {
+            { setEnabled(profileMethodEnabled() && userValue != null && aggregation == CPUResultsSnapshot.METHOD_LEVEL_VIEW && CPUTableView.isSelectable(userValue)); }
             protected void fireActionPerformed(ActionEvent e) { profileMethod(userValue); }
         });
         
-        popup.add(new JMenuItem(CPUView.ACTION_PROFILE_CLASS) {
+        if (profileClassSupported()) popup.add(new JMenuItem(CPUView.ACTION_PROFILE_CLASS) {
             { setEnabled(userValue != null && aggregation != CPUResultsSnapshot.PACKAGE_LEVEL_VIEW); }
             protected void fireActionPerformed(ActionEvent e) { profileClass(userValue); }
         });
         
+        if (profileMethodSupported() || profileClassSupported()) popup.addSeparator();
+        
+        JMenuItem[] customItems = invoker.createCustomMenuItems(this, value, userValue);
+        if (customItems != null) {
+            for (JMenuItem customItem : customItems) popup.add(customItem);
+            popup.addSeparator();
+        }
+        
         customizeNodePopup(invoker, popup, value, userValue);
         
-        popup.addSeparator();
         if (invoker == forwardCallsView) {
             final ProfilerTreeTable ttable = (ProfilerTreeTable)forwardCallsView.getResultsComponent();
             int column = ttable.convertColumnIndexToView(ttable.getMainColumn());
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/SubtreeCallGraphPanel.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/SubtreeCallGraphPanel.java
index f8229f94f..4a22b947e 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/SubtreeCallGraphPanel.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/cpu/SubtreeCallGraphPanel.java
@@ -214,7 +214,7 @@ public void setDataToDisplay(CPUResultsSnapshot snapshot, PrestimeCPUCCTNode nod
         super.setDataToDisplay(snapshot, view);
         this.rootNode = ((PrestimeCPUCCTNodeBacked)node).createRootCopy();
         if (popupShowSource != null) popupShowSource.setEnabled(isShowSourceAvailable());
-        popupAddToRoots.setEnabled(isAddToRootsAvailable());
+        if (popupAddToRoots != null) popupAddToRoots.setEnabled(isAddToRootsAvailable());
     }
 
     // NOTE: this method only sets sortingColumn and sortOrder, it doesn't refresh UI!
@@ -755,7 +755,7 @@ private void enableDisablePopup(PrestimeCPUCCTNode node) {
         if (popupShowSource != null) popupShowSource.setEnabled(regularNode && isShowSourceAvailable());
         if (popupShowSubtree != null) popupShowSubtree.setEnabled(regularNode);
         if (popupShowReverse != null) popupShowReverse.setEnabled(regularNode);
-        popupAddToRoots.setEnabled(regularNode && isAddToRootsAvailable());
+        if (popupAddToRoots != null) popupAddToRoots.setEnabled(regularNode && isAddToRootsAvailable());
         popupFind.setEnabled(regularNode);
     }
 
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/jdbc/LiveJDBCView.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/jdbc/LiveJDBCView.java
index 028ad7fc3..c79becfc8 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/jdbc/LiveJDBCView.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/jdbc/LiveJDBCView.java
@@ -22,12 +22,9 @@
 import java.awt.BorderLayout;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
-import java.util.Collection;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 import javax.swing.AbstractAction;
 import javax.swing.ActionMap;
 import javax.swing.JMenu;
@@ -37,32 +34,22 @@
 import javax.swing.SwingUtilities;
 import org.netbeans.lib.profiler.ProfilerClient;
 import org.netbeans.lib.profiler.client.ClientUtils;
-import org.netbeans.lib.profiler.results.RuntimeCCTNode;
-import org.netbeans.lib.profiler.results.jdbc.JdbcCCTProvider;
 import org.netbeans.lib.profiler.results.jdbc.JdbcResultsDiff;
 import org.netbeans.lib.profiler.results.jdbc.JdbcResultsSnapshot;
 import org.netbeans.lib.profiler.results.memory.PresoObjAllocCCTNode;
 import org.netbeans.lib.profiler.ui.UIUtils;
-import static org.netbeans.lib.profiler.ui.jdbc.JDBCTreeTableView.isSQL;
-import org.netbeans.lib.profiler.ui.memory.LiveMemoryView;
 import org.netbeans.lib.profiler.ui.results.DataView;
 import org.netbeans.lib.profiler.ui.swing.FilterUtils;
 import org.netbeans.lib.profiler.ui.swing.ProfilerTable;
 import org.netbeans.lib.profiler.ui.swing.ProfilerTreeTable;
 import org.netbeans.lib.profiler.ui.swing.SearchUtils;
 import org.netbeans.lib.profiler.utils.Wildcards;
-import org.openide.util.Lookup;
 
 /**
  *
  * @author Jiri Sedlacek
  */
 public abstract class LiveJDBCView extends JPanel {
-
-    private static final int MIN_UPDATE_DIFF = 900;
-    private static final int MAX_UPDATE_DIFF = 1400;
-    
-    private ResultsMonitor rm;
     
     private JdbcResultsSnapshot snapshot;
     private JdbcResultsSnapshot refSnapshot;
@@ -71,33 +58,11 @@
     private JDBCTreeTableView jdbcCallsView;
     
     private long lastupdate;
-    private volatile boolean paused;
-    private volatile boolean forceRefresh;
     private volatile boolean refreshIsRunning;
     
     private ExecutorService executor;
     
     
-    private final class ResultsMonitor implements JdbcCCTProvider.Listener {
-        
-        @Override
-        public void cctEstablished(RuntimeCCTNode appRootNode, boolean empty) {
-            if (!empty) {
-                try {
-                    LiveJDBCView.this.refreshData(appRootNode);
-                } catch (ClientUtils.TargetAppOrVMTerminated ex) {
-                    Logger.getLogger(LiveMemoryView.class.getName()).log(Level.FINE, null, ex);
-                }
-            }
-        }
-
-        @Override
-        public void cctReset() {
-            LiveJDBCView.this.resetData();
-        }
-    }
-    
-    
     public LiveJDBCView(Set<ClientUtils.SourceCodeSelection> selection) {
         initUI(selection);
         registerActions();        
@@ -108,68 +73,51 @@ public void setView(boolean forwardCalls, boolean hotSpots, boolean reverseCalls
         jdbcCallsView.setVisible(forwardCalls);
     }
     
-    public void setPaused(boolean paused) {
-        this.paused = paused;
+    public boolean isRefreshRunning() {
+        return refreshIsRunning;
     }
-
-    public void setForceRefresh(boolean forceRefresh) {
-        this.forceRefresh = forceRefresh;
+    
+    public long getLastUpdate() {
+        return lastupdate;
     }
-
-    private void refreshData(RuntimeCCTNode appRootNode) throws ClientUtils.TargetAppOrVMTerminated {
-        if ((lastupdate + MIN_UPDATE_DIFF > System.currentTimeMillis() || paused) && !forceRefresh) return;
+    
+    public void setData(final JdbcResultsSnapshot snapshotData) {
         if (refreshIsRunning) return;
         refreshIsRunning = true;
-        try {
-            ProfilerClient client = getProfilerClient();
-            final JdbcResultsSnapshot snapshotData =
-                    client.getStatus().getInstrMethodClasses() == null ?
-                    null : client.getJdbcProfilingResultsSnapshot(false);
-            UIUtils.runInEventDispatchThread(new Runnable() {
-                public void run() {
-                    snapshot = snapshotData;
-                    setData();
-                    lastupdate = System.currentTimeMillis();
-                    forceRefresh = false;
-                }
-            });
-        } catch (Throwable t) {
-            refreshIsRunning = false;
-            if (t instanceof ClientUtils.TargetAppOrVMTerminated) {
-                throw ((ClientUtils.TargetAppOrVMTerminated)t);
-            } else {
-                Logger.getLogger(LiveJDBCView.class.getName()).log(Level.SEVERE, null, t);
+        
+        UIUtils.runInEventDispatchThread(new Runnable() {
+            public void run() {
+                snapshot = snapshotData;
+                
+                setData();
             }
-        }
+        });
     }
     
     private void setData() {
-        UIUtils.runInEventDispatchThread(new Runnable() {
-            public void run() {
-                if (snapshot == null) {
-                    resetData();
-                    refreshIsRunning = false;
-                } else {
-                    getExecutor().submit(new Runnable() {
-                        public void run() {
-                            final JdbcResultsSnapshot _snapshot = refSnapshot == null ? snapshot :
-                                                                 refSnapshot.createDiff(snapshot);
+        if (snapshot == null) {
+            resetData();
+            refreshIsRunning = false;
+        } else {
+            getExecutor().submit(new Runnable() {
+                public void run() {
+                    final JdbcResultsSnapshot _snapshot = refSnapshot == null ? snapshot :
+                                                         refSnapshot.createDiff(snapshot);
 
-                            SwingUtilities.invokeLater(new Runnable() {
-                                public void run() {
-                                    try {
-                                        boolean diff = _snapshot instanceof JdbcResultsDiff;
-                                        jdbcCallsView.setData(_snapshot, null, -1, null, false, false, diff);
-                                    } finally {
-                                        refreshIsRunning = false;
-                                    }
-                                }
-                            });
+                    SwingUtilities.invokeLater(new Runnable() {
+                        public void run() {
+                            try {
+                                boolean diff = _snapshot instanceof JdbcResultsDiff;
+                                jdbcCallsView.setData(_snapshot, null, -1, null, false, false, diff);
+                            } finally {
+                                refreshIsRunning = false;
+                                lastupdate = System.currentTimeMillis();
+                            }
                         }
                     });
                 }
-            }
-        });
+            });
+        }
     }
     
     public boolean setDiffView(final boolean diff) {
@@ -183,12 +131,6 @@ public void run() {
         return true;
     }
     
-    public void refreshData() throws ClientUtils.TargetAppOrVMTerminated {
-        if ((lastupdate + MAX_UPDATE_DIFF < System.currentTimeMillis() && !paused) || forceRefresh) {
-            getProfilerClient().forceObtainedResultsDump(true);
-        }        
-    }
-    
     public void resetData() {
         UIUtils.runInEventDispatchThread(new Runnable() {
             public void run() {
@@ -213,34 +155,13 @@ public void cleanup() {
     }
     
     
-    public void profilingSessionStarted() {
-        if (rm == null) {
-            rm = new ResultsMonitor();
-            Collection<? extends JdbcCCTProvider> jdbcCCTProviders = Lookup.getDefault().lookupAll(JdbcCCTProvider.class);
-            assert !jdbcCCTProviders.isEmpty();
-            for (JdbcCCTProvider provider : jdbcCCTProviders) {
-                provider.addListener(rm);
-            }
-        }    
-    }
-    
-    public void profilingSessionFinished() {
-        if (rm != null) {
-            Collection<? extends JdbcCCTProvider> jdbcCCTProviders = Lookup.getDefault().lookupAll(JdbcCCTProvider.class);
-            assert !jdbcCCTProviders.isEmpty();
-            for (JdbcCCTProvider provider : jdbcCCTProviders) {
-                provider.removeListener(rm);
-            }
-            rm = null;
-        }
-    }
-    
-    
     protected abstract ProfilerClient getProfilerClient();
     
     
     protected boolean profileMethodSupported() { return true; }
     
+    protected boolean profileClassSupported() { return true; }
+    
     
     protected abstract boolean showSourceSupported();
     
@@ -352,17 +273,25 @@ private void populatePopup(final DataView invoker, JPopupMenu popup, final Objec
             popup.addSeparator();
         }
         
-        popup.add(new JMenuItem(JDBCView.ACTION_PROFILE_METHOD) {
+        if (profileMethodSupported()) popup.add(new JMenuItem(JDBCView.ACTION_PROFILE_METHOD) {
             { setEnabled(userValue != null && JDBCTreeTableView.isSelectable(node)); }
             protected void fireActionPerformed(ActionEvent e) { profileMethod(userValue); }
         });
         
-        popup.add(new JMenuItem(JDBCView.ACTION_PROFILE_CLASS) {
+        if (profileClassSupported()) popup.add(new JMenuItem(JDBCView.ACTION_PROFILE_CLASS) {
             { setEnabled(userValue != null); }
             protected void fireActionPerformed(ActionEvent e) { profileClass(userValue); }
         });
         
-        popup.addSeparator();
+        if (profileMethodSupported() || profileClassSupported()) popup.addSeparator();
+        
+        JMenuItem[] customItems = invoker.createCustomMenuItems(this, value, userValue);
+        if (customItems != null) {
+            for (JMenuItem customItem : customItems) popup.add(customItem);
+            popup.addSeparator();
+        }
+        
+        customizeNodePopup(invoker, popup, value, userValue);
         
         final ProfilerTreeTable ttable = (ProfilerTreeTable)jdbcCallsView.getResultsComponent();
         JMenu expand = new JMenu(JDBCView.EXPAND_MENU);
@@ -406,6 +335,9 @@ protected void fireActionPerformed(ActionEvent e) {
         });
     }
     
+    protected void customizeNodePopup(DataView invoker, JPopupMenu popup, Object value, ClientUtils.SourceCodeSelection userValue) {}
+    
+    
     private synchronized ExecutorService getExecutor() {
         if (executor == null) executor = Executors.newSingleThreadExecutor();
         return executor;
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/jdbc/LiveJDBCViewUpdater.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/jdbc/LiveJDBCViewUpdater.java
new file mode 100644
index 000000000..291ae81d3
--- /dev/null
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/jdbc/LiveJDBCViewUpdater.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.lib.profiler.ui.jdbc;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.lib.profiler.ProfilerClient;
+import org.netbeans.lib.profiler.client.ClientUtils;
+import org.netbeans.lib.profiler.results.RuntimeCCTNode;
+import org.netbeans.lib.profiler.results.jdbc.JdbcCCTProvider;
+import org.netbeans.lib.profiler.results.jdbc.JdbcResultsSnapshot;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Jiri
+ */
+public class LiveJDBCViewUpdater {
+    
+    private static final int MIN_UPDATE_DIFF = 900;
+    private static final int MAX_UPDATE_DIFF = 1400;
+    
+    
+    private CCTHandler handler;
+    
+    private final LiveJDBCView jdbcView;
+    private final ProfilerClient client;
+    
+    private volatile boolean paused;
+    private volatile boolean forceRefresh;
+    
+    
+    
+    public LiveJDBCViewUpdater(LiveJDBCView jdbcView, ProfilerClient client) {
+        this.jdbcView = jdbcView;
+        this.client = client;
+    }
+    
+    
+    
+    public void setPaused(boolean paused) {
+        this.paused = paused;
+    }
+
+    public void setForceRefresh(boolean forceRefresh) {
+        this.forceRefresh = forceRefresh;
+    }
+    
+    public void update() throws ClientUtils.TargetAppOrVMTerminated {
+        if (handler == null) handler = CCTHandler.registerUpdater(this);
+        
+        if (forceRefresh || (!paused && jdbcView.getLastUpdate() + MAX_UPDATE_DIFF < System.currentTimeMillis()))
+            client.forceObtainedResultsDump(true);
+    }
+    
+    public void cleanup() {
+        if (handler != null) handler.unregisterUpdater(this);
+        handler = null;
+    }
+    
+    
+    private void updateData() throws ClientUtils.TargetAppOrVMTerminated {
+        if (!forceRefresh && (paused || jdbcView.getLastUpdate() + MIN_UPDATE_DIFF > System.currentTimeMillis())) return;
+        
+        JdbcResultsSnapshot data = client.getStatus().getInstrMethodClasses() == null ?
+                            null : client.getJdbcProfilingResultsSnapshot(false);
+        jdbcView.setData(data);
+        
+        forceRefresh = false;
+    }
+    
+    private void resetData() {
+        jdbcView.resetData();
+    }
+    
+    
+    @ServiceProvider(service=JdbcCCTProvider.Listener.class)
+    public static class CCTHandler implements JdbcCCTProvider.Listener {
+        
+        private final List<LiveJDBCViewUpdater> updaters = new ArrayList();
+        
+        
+        public static CCTHandler registerUpdater(LiveJDBCViewUpdater updater) {
+            CCTHandler handler = Lookup.getDefault().lookup(CCTHandler.class);
+            
+            if (handler.updaters.isEmpty()) {
+                Collection<? extends JdbcCCTProvider> jdbcCCTProviders = Lookup.getDefault().lookupAll(JdbcCCTProvider.class);
+                assert !jdbcCCTProviders.isEmpty();
+                for (JdbcCCTProvider provider : jdbcCCTProviders) provider.addListener(handler);
+            }
+            
+            handler.updaters.add(updater);
+            return handler;
+        }
+        
+        public void unregisterUpdater(LiveJDBCViewUpdater updater) {
+            updaters.remove(updater);
+            
+            if (updaters.isEmpty()) {
+                Collection<? extends JdbcCCTProvider> jdbcCCTProviders = Lookup.getDefault().lookupAll(JdbcCCTProvider.class);
+                assert !jdbcCCTProviders.isEmpty();
+                for (JdbcCCTProvider provider : jdbcCCTProviders) provider.removeListener(this);
+            }
+        }
+        
+
+        public final void cctEstablished(RuntimeCCTNode appRootNode, boolean empty) {
+           if (!empty) {
+                for (LiveJDBCViewUpdater updater : updaters) try {
+                    updater.updateData();
+                } catch (ClientUtils.TargetAppOrVMTerminated ex) {
+                    Logger.getLogger(LiveJDBCView.class.getName()).log(Level.FINE, null, ex);
+                }
+            }
+        }
+
+        public final void cctReset() {
+            for (LiveJDBCViewUpdater updater : updaters) updater.resetData();
+        }
+
+    }
+    
+}
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/AllocTreeTableView.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/AllocTreeTableView.java
index 7af16418b..63b14ebc1 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/AllocTreeTableView.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/AllocTreeTableView.java
@@ -310,6 +310,10 @@ public void refreshSelection() {
     
     protected abstract void populatePopup(JPopupMenu popup, Object value, ClientUtils.SourceCodeSelection userValue);
     
+    protected void popupShowing() {};
+    
+    protected void popupHidden()  {};
+    
     
     private HideableBarRenderer[] renderers;
     
@@ -325,6 +329,12 @@ private void initUI() {
             protected void populatePopup(JPopupMenu popup, Object value, Object userValue) {
                 AllocTreeTableView.this.populatePopup(popup, value, (ClientUtils.SourceCodeSelection)userValue);
             }
+            protected void popupShowing() {
+                AllocTreeTableView.this.popupShowing();
+            }
+            protected void popupHidden() {
+                AllocTreeTableView.this.popupHidden();
+            }
         };
         
         treeTable.setColumnToolTips(selection == null ? new String[] {
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LiveMemoryView.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LiveMemoryView.java
index 23af2e118..43bbd8655 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LiveMemoryView.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LiveMemoryView.java
@@ -23,32 +23,24 @@
 import java.awt.Font;
 import java.awt.event.ActionEvent;
 import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
 import javax.swing.AbstractAction;
 import javax.swing.ActionMap;
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
-import javax.swing.SwingUtilities;
-import org.netbeans.lib.profiler.ProfilerClient;
 import org.netbeans.lib.profiler.client.ClientUtils;
 import org.netbeans.lib.profiler.filters.GenericFilter;
-import org.netbeans.lib.profiler.global.CommonConstants;
-import org.netbeans.lib.profiler.results.RuntimeCCTNode;
 import org.netbeans.lib.profiler.results.cpu.CPUResultsSnapshot;
 import org.netbeans.lib.profiler.results.memory.AllocMemoryResultsSnapshot;
 import org.netbeans.lib.profiler.results.memory.LivenessMemoryResultsSnapshot;
-import org.netbeans.lib.profiler.results.memory.MemoryCCTProvider;
 import org.netbeans.lib.profiler.results.memory.MemoryResultsSnapshot;
 import org.netbeans.lib.profiler.results.memory.SampledMemoryResultsSnapshot;
+import org.netbeans.lib.profiler.ui.UIUtils;
 import org.netbeans.lib.profiler.ui.results.DataView;
 import org.netbeans.lib.profiler.ui.swing.FilterUtils;
 import org.netbeans.lib.profiler.ui.swing.ProfilerTreeTable;
 import org.netbeans.lib.profiler.ui.swing.SearchUtils;
-import org.openide.util.Lookup;
-import org.openide.util.lookup.ServiceProvider;
 
 /**
  *
@@ -56,18 +48,12 @@
  */
 public abstract class LiveMemoryView extends JPanel {
     
-    private static final int MIN_UPDATE_DIFF = 900;
-    private static final int MAX_UPDATE_DIFF = 1400;
-    
     private MemoryView dataView;
     
     private long lastupdate;
-    private volatile boolean paused;
-    private volatile boolean forceRefresh;
     private volatile boolean refreshIsRunning;
     
     private final Set<ClientUtils.SourceCodeSelection> selection;
-    private final ResultsMonitor rm;
     
     private MemoryResultsSnapshot snapshot;
     private MemoryResultsSnapshot refSnapshot;
@@ -75,122 +61,134 @@
     private GenericFilter filter;
     
     
-    @ServiceProvider(service=MemoryCCTProvider.Listener.class)
-    public static final class ResultsMonitor implements MemoryCCTProvider.Listener {
-
-        private LiveMemoryView view;
-        
-        @Override
-        public void cctEstablished(RuntimeCCTNode appRootNode, boolean empty) {
-            if (view != null && !empty) {
-                try {
-                    view.refreshData(appRootNode);
-                } catch (ClientUtils.TargetAppOrVMTerminated ex) {
-                    Logger.getLogger(LiveMemoryView.class.getName()).log(Level.FINE, null, ex);
-                }
-            }
-        }
-
-        @Override
-        public void cctReset() {
-            if (view != null) {
-                view.resetData();
-            }
-        }
-    }
+    
        
     public LiveMemoryView(Set<ClientUtils.SourceCodeSelection> selection) {
         this.selection = selection;
-        
         initUI();
-        rm = Lookup.getDefault().lookup(ResultsMonitor.class);
-        rm.view = this;
     }
     
-    private void refreshData(RuntimeCCTNode appRootNode) throws ClientUtils.TargetAppOrVMTerminated {
-        if ((lastupdate + MIN_UPDATE_DIFF > System.currentTimeMillis() || paused) && !forceRefresh) return;
+    
+    
+    public boolean isRefreshRunning() {
+        return refreshIsRunning;
+    }
+    
+    public long getLastUpdate() {
+        return lastupdate;
+    }
+    
+    public void setData(final MemoryResultsSnapshot snapshotData, final GenericFilter ifilter) {
         if (refreshIsRunning) return;
         refreshIsRunning = true;
-        try {
-            ProfilerClient client = getProfilerClient();
-            final MemoryResultsSnapshot _snapshot = client.getMemoryProfilingResultsSnapshot(false);
+        
+        // class names in VM format
+//        MemoryView.userFormClassNames(snapshotData);
+        
+        UIUtils.runInEventDispatchThread(new Runnable() {
+            public void run() {
+                try {
+                    updateDataView(snapshotData);
 
-            // class names in VM format
-            MemoryView.userFormClassNames(_snapshot);
+                    snapshot = snapshotData;
+                    filter = ifilter;
 
-            // class names in VM format
-            final GenericFilter ifilter = client.getSettings().getInstrumentationFilter();
-            // --- TODO: rewrite down to all usages
-//            String[] _ifilter = ifilter == null ? null : ifilter.getUserFilterStrings();
-//            final Collection<String> _filter = _ifilter == null ? Collections.EMPTY_LIST :
-//                                               Arrays.asList(_ifilter);
-            // ---
-//            final Collection<String> _filter = Arrays.asList(ifilter.getValues()); // Actually wrong, cuts trailing *
-            
-            
-//            if (_ifilter != null) for (String s : _ifilter)
-//                    _filter.add(StringUtils.userFormClassName(s));
-
-            SwingUtilities.invokeLater(new Runnable() {
-                public void run() {
-                    try {
-                        refreshDataImpl(_snapshot, ifilter);
-                    } finally {
-                        refreshIsRunning = false;
+                    if (dataView != null && snapshot != null) {
+                        if (refSnapshot == null) dataView.setData(snapshot, filter, CPUResultsSnapshot.CLASS_LEVEL_VIEW);
+                        else dataView.setData(refSnapshot.createDiff(snapshot), filter, CPUResultsSnapshot.CLASS_LEVEL_VIEW);
                     }
+                } finally {
+                    refreshIsRunning = false;
+                    lastupdate = System.currentTimeMillis();
                 }
-            });
-        } catch (RuntimeException ex) {
-            refreshIsRunning = false;
-            throw ex;
-        } catch (ClientUtils.TargetAppOrVMTerminated ex) {
-            refreshIsRunning = false;
-            throw ex;            
-        }
-        
-        lastupdate = System.currentTimeMillis();
-        forceRefresh = false;
+            }
+        });
     }
     
-    private void refreshDataImpl(MemoryResultsSnapshot _snapshot, GenericFilter _filter) {
-        assert SwingUtilities.isEventDispatchThread();
-        
-        updateDataView(_snapshot);
-        
-        snapshot = _snapshot;
-        filter = _filter;
-        
-        if (dataView != null && snapshot != null) {
-            if (refSnapshot == null) dataView.setData(snapshot, filter, CPUResultsSnapshot.CLASS_LEVEL_VIEW);
-            else dataView.setData(refSnapshot.createDiff(snapshot), filter, CPUResultsSnapshot.CLASS_LEVEL_VIEW);
-        }
-    }
+//    private void refreshData(RuntimeCCTNode appRootNode) throws ClientUtils.TargetAppOrVMTerminated {
+//        if ((lastupdate + MIN_UPDATE_DIFF > System.currentTimeMillis() || paused) && !forceRefresh) return;
+//        if (refreshIsRunning) return;
+//        refreshIsRunning = true;
+//        try {
+//            ProfilerClient client = getProfilerClient();
+//            final MemoryResultsSnapshot _snapshot = client.getMemoryProfilingResultsSnapshot(false);
+//
+//            // class names in VM format
+//            MemoryView.userFormClassNames(_snapshot);
+//
+//            // class names in VM format
+//            final GenericFilter ifilter = client.getSettings().getInstrumentationFilter();
+//            // --- TODO: rewrite down to all usages
+////            String[] _ifilter = ifilter == null ? null : ifilter.getUserFilterStrings();
+////            final Collection<String> _filter = _ifilter == null ? Collections.EMPTY_LIST :
+////                                               Arrays.asList(_ifilter);
+//            // ---
+////            final Collection<String> _filter = Arrays.asList(ifilter.getValues()); // Actually wrong, cuts trailing *
+//            
+//            
+////            if (_ifilter != null) for (String s : _ifilter)
+////                    _filter.add(StringUtils.userFormClassName(s));
+//
+//            SwingUtilities.invokeLater(new Runnable() {
+//                public void run() {
+//                    try {
+//                        refreshDataImpl(_snapshot, ifilter);
+//                    } finally {
+//                        refreshIsRunning = false;
+//                    }
+//                }
+//            });
+//        } catch (RuntimeException ex) {
+//            refreshIsRunning = false;
+//            throw ex;
+//        } catch (ClientUtils.TargetAppOrVMTerminated ex) {
+//            refreshIsRunning = false;
+//            throw ex;            
+//        }
+//        
+//        lastupdate = System.currentTimeMillis();
+//        forceRefresh = false;
+//    }
+    
+//    private void refreshDataImpl(MemoryResultsSnapshot _snapshot, GenericFilter _filter) {
+//        assert SwingUtilities.isEventDispatchThread();
+//        
+//        updateDataView(_snapshot);
+//        
+//        snapshot = _snapshot;
+//        filter = _filter;
+//        
+//        if (dataView != null && snapshot != null) {
+//            if (refSnapshot == null) dataView.setData(snapshot, filter, CPUResultsSnapshot.CLASS_LEVEL_VIEW);
+//            else dataView.setData(refSnapshot.createDiff(snapshot), filter, CPUResultsSnapshot.CLASS_LEVEL_VIEW);
+//        }
+//    }
     
     public boolean setDiffView(boolean diff) {
         if (snapshot == null) return false;
         refSnapshot = diff ? snapshot : null;
-        refreshDataImpl(snapshot, filter);
+        setData(snapshot, filter);
         return true;
     }
 
-    public void refreshData() throws ClientUtils.TargetAppOrVMTerminated {
-        if (paused && !forceRefresh) return;
-        
-        ProfilerClient client = getProfilerClient();
-        switch (client.getCurrentInstrType()) {
-            case CommonConstants.INSTR_NONE_MEMORY_SAMPLING:
-                refreshData(null);
-                break;
-            case CommonConstants.INSTR_OBJECT_LIVENESS:
-            case CommonConstants.INSTR_OBJECT_ALLOCATIONS:
-                if (lastupdate + MAX_UPDATE_DIFF < System.currentTimeMillis()) {
-                    client.forceObtainedResultsDump(true);
-                }
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid profiling instr. type: " + client.getCurrentInstrType()); // NOI18N
-        }
-    }
+//    public void refreshData() throws ClientUtils.TargetAppOrVMTerminated {
+//        if (paused && !forceRefresh) return;
+//        
+//        ProfilerClient client = getProfilerClient();
+//        switch (client.getCurrentInstrType()) {
+//            case CommonConstants.INSTR_NONE_MEMORY_SAMPLING:
+//                refreshData(null);
+//                break;
+//            case CommonConstants.INSTR_OBJECT_LIVENESS:
+//            case CommonConstants.INSTR_OBJECT_ALLOCATIONS:
+//                if (lastupdate + MAX_UPDATE_DIFF < System.currentTimeMillis()) {
+//                    client.forceObtainedResultsDump(true);
+//                }
+//                break;
+//            default:
+//                throw new IllegalArgumentException("Invalid profiling instr. type: " + client.getCurrentInstrType()); // NOI18N
+//        }
+//    }
     
     public void resetData() {
         if (dataView != null) dataView.resetData();
@@ -199,14 +197,6 @@ public void resetData() {
         filter = null;
     }
 
-    public void setPaused(boolean paused) {
-        this.paused = paused;
-    }
-
-    public void setForceRefresh(boolean forceRefresh) {
-        this.forceRefresh = forceRefresh;
-    }
-    
     
     public void showSelectionColumn() {
         if (dataView != null) dataView.showSelectionColumn();
@@ -217,12 +207,14 @@ public void refreshSelection() {
     }
     
     
-    public void cleanup() {
-        if (rm.view == this) rm.view = null;
-    }
+//    public void cleanup() {
+//        if (rm.view == this) rm.view = null;
+//    }
+    
     
+//    protected abstract ProfilerClient getProfilerClient();
     
-    protected abstract ProfilerClient getProfilerClient();
+    protected boolean profileClassSupported() { return true; }
     
     
     protected abstract boolean showSourceSupported();
@@ -331,12 +323,22 @@ private void populatePopup(final DataView invoker, JPopupMenu popup, Object valu
             popup.addSeparator();
         }
         
-        popup.add(new JMenuItem(MemoryView.ACTION_PROFILE_CLASS) {
-            { setEnabled(userValue != null); }
-            protected void fireActionPerformed(ActionEvent e) { selectForProfiling(userValue); }
-        });
+        if (profileClassSupported()) {
+            popup.add(new JMenuItem(MemoryView.ACTION_PROFILE_CLASS) {
+                { setEnabled(userValue != null); }
+                protected void fireActionPerformed(ActionEvent e) { selectForProfiling(userValue); }
+            });
+        }
         
-        popup.addSeparator();
+        if (profileClassSupported()) popup.addSeparator();
+        
+        JMenuItem[] customItems = invoker.createCustomMenuItems(this, value, userValue);
+        if (customItems != null) {
+            for (JMenuItem customItem : customItems) popup.add(customItem);
+            popup.addSeparator();
+        }
+        
+        customizeNodePopup(invoker, popup, value, userValue);
         
         if (snapshot.containsStacks()) {
             final ProfilerTreeTable ttable = (ProfilerTreeTable)dataView.getResultsComponent();
@@ -368,12 +370,13 @@ protected void fireActionPerformed(ActionEvent e) {
                     ttable.collapseAll();
                 }
             });
+            
+            popup.addSeparator();
         }
         
-        popup.addSeparator();
         popup.add(invoker.createCopyMenuItem());
-        
         popup.addSeparator();
+        
         popup.add(new JMenuItem(FilterUtils.ACTION_FILTER) {
             protected void fireActionPerformed(ActionEvent e) { invoker.activateFilter(); }
         });
@@ -382,6 +385,8 @@ protected void fireActionPerformed(ActionEvent e) {
         });
     }
     
+    protected void customizeNodePopup(DataView invoker, JPopupMenu popup, Object value, ClientUtils.SourceCodeSelection userValue) {}
+    
     
     private void initUI() {
         setLayout(new BorderLayout(0, 0));
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LiveMemoryViewUpdater.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LiveMemoryViewUpdater.java
new file mode 100644
index 000000000..46ed6f66a
--- /dev/null
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LiveMemoryViewUpdater.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.lib.profiler.ui.memory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.lib.profiler.ProfilerClient;
+import org.netbeans.lib.profiler.client.ClientUtils;
+import org.netbeans.lib.profiler.filters.GenericFilter;
+import org.netbeans.lib.profiler.global.CommonConstants;
+import org.netbeans.lib.profiler.results.RuntimeCCTNode;
+import org.netbeans.lib.profiler.results.memory.MemoryCCTProvider;
+import org.netbeans.lib.profiler.results.memory.MemoryResultsSnapshot;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Jiri Sedlacek
+ */
+public class LiveMemoryViewUpdater {
+    
+    private static final int MIN_UPDATE_DIFF = 900;
+    private static final int MAX_UPDATE_DIFF = 1400;
+    
+    
+    private CCTHandler handler;
+    
+    private final LiveMemoryView memoryView;
+    private final ProfilerClient client;
+    
+    private volatile boolean paused;
+    private volatile boolean forceRefresh;
+    
+    
+    public LiveMemoryViewUpdater(LiveMemoryView memoryView, ProfilerClient client) {
+        this.memoryView = memoryView;
+        this.client = client;
+        
+        handler = CCTHandler.registerUpdater(this);
+    }
+    
+    
+    public void setPaused(boolean paused) {
+        this.paused = paused;
+    }
+
+    public void setForceRefresh(boolean forceRefresh) {
+        this.forceRefresh = forceRefresh;
+    }
+    
+    public void update() throws ClientUtils.TargetAppOrVMTerminated {
+        if (forceRefresh || (!paused && memoryView.getLastUpdate() + MAX_UPDATE_DIFF < System.currentTimeMillis()))
+            switch (client.getCurrentInstrType()) {
+                case CommonConstants.INSTR_NONE_MEMORY_SAMPLING:
+                    updateData();
+                    break;
+                case CommonConstants.INSTR_OBJECT_LIVENESS:
+                case CommonConstants.INSTR_OBJECT_ALLOCATIONS:
+                    if (memoryView.getLastUpdate() + MAX_UPDATE_DIFF < System.currentTimeMillis()) {
+                        client.forceObtainedResultsDump(true);
+                    }
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid profiling instr. type: " + client.getCurrentInstrType()); // NOI18N
+            }
+    }
+    
+    public void cleanup() {
+        handler.unregisterUpdater(this);
+        handler = null;
+    }
+    
+    
+    private void updateData() throws ClientUtils.TargetAppOrVMTerminated {
+        if (!forceRefresh && (paused || memoryView.getLastUpdate() + MIN_UPDATE_DIFF > System.currentTimeMillis())) return;
+        
+        MemoryResultsSnapshot snapshot = client.getMemoryProfilingResultsSnapshot(false);
+
+        // class names in VM format
+        MemoryView.userFormClassNames(snapshot);
+
+        // class names in VM format
+        GenericFilter filter = client.getSettings().getInstrumentationFilter();
+        
+        memoryView.setData(snapshot, filter);
+        
+        forceRefresh = false;
+    }
+    
+    private void resetData() {
+        memoryView.resetData();
+    }
+    
+    
+    @ServiceProvider(service=MemoryCCTProvider.Listener.class)
+    public static final class CCTHandler implements MemoryCCTProvider.Listener {
+
+        private final List<LiveMemoryViewUpdater> updaters = new ArrayList();
+        
+        
+        public static CCTHandler registerUpdater(LiveMemoryViewUpdater updater) {
+            CCTHandler handler = Lookup.getDefault().lookup(CCTHandler.class);
+            handler.updaters.add(updater);
+            return handler;
+        }
+        
+        public void unregisterUpdater(LiveMemoryViewUpdater updater) {
+            updaters.remove(updater);
+        }
+        
+        
+        public void cctEstablished(RuntimeCCTNode appRootNode, boolean empty) {
+            if (!empty) {
+                for (LiveMemoryViewUpdater updater : updaters) try {
+                    updater.updateData();
+                } catch (ClientUtils.TargetAppOrVMTerminated ex) {
+//                } catch (CPUResultsSnapshot.NoDataAvailableException ex) {
+                    Logger.getLogger(LiveMemoryView.class.getName()).log(Level.FINE, null, ex);
+                }
+            }
+        }
+
+        public void cctReset() {
+            for (LiveMemoryViewUpdater updater : updaters) updater.resetData();
+        }
+    }
+    
+}
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LivenessResultsPanel.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LivenessResultsPanel.java
index f00828c98..4fdadd1b9 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LivenessResultsPanel.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LivenessResultsPanel.java
@@ -43,6 +43,7 @@
 import javax.swing.*;
 import javax.swing.table.TableCellRenderer;
 import javax.swing.table.TableColumnModel;
+import org.netbeans.lib.profiler.ui.AppearanceController;
 
 
 /**
@@ -383,7 +384,10 @@ public boolean getInitialSorting(int column) {
                     }
                 });
 
-            resTableModel.setRealColumnVisibility(7, false);
+            int[] unvisible = AppearanceController.getDefault().invisibleLivenessResultsColumns();
+            for (int c : unvisible) {
+                resTableModel.setRealColumnVisibility(c, false);
+            }
 
             resTable = new JExtendedTable(resTableModel) {
                     public void doLayout() {
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LivenessTreeTableView.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LivenessTreeTableView.java
index 118af23a5..ec81dcf98 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LivenessTreeTableView.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/LivenessTreeTableView.java
@@ -356,6 +356,10 @@ public void refreshSelection() {
     
     protected abstract void populatePopup(JPopupMenu popup, Object value, ClientUtils.SourceCodeSelection userValue);
     
+    protected void popupShowing() {};
+    
+    protected void popupHidden()  {};
+    
     
     private HideableBarRenderer[] renderers;
     private NumberRenderer[] renderersEx;
@@ -372,6 +376,12 @@ private void initUI() {
             protected void populatePopup(JPopupMenu popup, Object value, Object userValue) {
                 LivenessTreeTableView.this.populatePopup(popup, value, (ClientUtils.SourceCodeSelection)userValue);
             }
+            protected void popupShowing() {
+                LivenessTreeTableView.this.popupShowing();
+            }
+            protected void popupHidden() {
+                LivenessTreeTableView.this.popupHidden();
+            }
         };
         
         treeTable.setColumnToolTips(selection == null ? new String[] {
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/SnapshotMemoryView.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/SnapshotMemoryView.java
index 1c4fe39bf..44cda46a9 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/SnapshotMemoryView.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/memory/SnapshotMemoryView.java
@@ -232,6 +232,11 @@ public void setRefSnapshot(MemoryResultsSnapshot snapshot) {
     }
     
     
+    protected boolean profileMethodSupported() { return true; }
+    
+    protected boolean profileClassSupported() { return true; }
+    
+    
     protected abstract boolean showSourceSupported();
     
     protected abstract void showSource(ClientUtils.SourceCodeSelection value);
@@ -273,19 +278,29 @@ private void populatePopup(final DataView invoker, JPopupMenu popup, Object valu
             popup.addSeparator();
         }
         
-        if (userValue == null || !Wildcards.ALLWILDCARD.equals(userValue.getMethodName())) {
-            popup.add(new JMenuItem(MemoryView.ACTION_PROFILE_METHOD) {
-                { setEnabled(userValue != null && isSelectable(userValue, true)); }
-                protected void fireActionPerformed(ActionEvent e) { profileMethod(userValue); }
-            });
+        if (profileMethodSupported()) {
+            if (userValue == null || !Wildcards.ALLWILDCARD.equals(userValue.getMethodName())) {
+                popup.add(new JMenuItem(MemoryView.ACTION_PROFILE_METHOD) {
+                    { setEnabled(userValue != null && isSelectable(userValue, true)); }
+                    protected void fireActionPerformed(ActionEvent e) { profileMethod(userValue); }
+                });
+            }
         }
         
-        popup.add(new JMenuItem(MemoryView.ACTION_PROFILE_CLASS) {
+        if (profileClassSupported()) popup.add(new JMenuItem(MemoryView.ACTION_PROFILE_CLASS) {
             { setEnabled(userValue != null && aggregation != CPUResultsSnapshot.PACKAGE_LEVEL_VIEW && isSelectable(userValue, false)); }
             protected void fireActionPerformed(ActionEvent e) { profileClass(userValue); }
         });
         
-        popup.addSeparator();
+        if (profileMethodSupported() || profileClassSupported()) popup.addSeparator();
+        
+        JMenuItem[] customItems = invoker.createCustomMenuItems(this, value, userValue);
+        if (customItems != null) {
+            for (JMenuItem customItem : customItems) popup.add(customItem);
+            popup.addSeparator();
+        }
+        
+        customizeNodePopup(invoker, popup, value, userValue);
         
         if (snapshot.containsStacks()) {
             final ProfilerTreeTable ttable = (ProfilerTreeTable)dataView.getResultsComponent();
@@ -317,12 +332,13 @@ protected void fireActionPerformed(ActionEvent e) {
                     ttable.collapseAll();
                 }
             });
+            
+            popup.addSeparator();
         }
         
-        popup.addSeparator();
         popup.add(invoker.createCopyMenuItem());
-        
         popup.addSeparator();
+        
         popup.add(new JMenuItem(FilterUtils.ACTION_FILTER) {
             protected void fireActionPerformed(ActionEvent e) { invoker.activateFilter(); }
         });
@@ -331,6 +347,8 @@ protected void fireActionPerformed(ActionEvent e) {
         });
     }
     
+    protected void customizeNodePopup(DataView invoker, JPopupMenu popup, Object value, ClientUtils.SourceCodeSelection userValue) {}
+    
     private void setAggregation(int aggregation) {
         this.aggregation = aggregation;
         if (dataView != null) {
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/results/DataView.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/results/DataView.java
index c7919c32b..5a393db9b 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/results/DataView.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/results/DataView.java
@@ -26,6 +26,10 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.FocusAdapter;
 import java.awt.event.FocusEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import javax.swing.AbstractAction;
 import javax.swing.Action;
 import javax.swing.JComponent;
@@ -39,6 +43,7 @@
 import org.netbeans.lib.profiler.ui.swing.ProfilerTable;
 import org.netbeans.lib.profiler.ui.swing.ProfilerTreeTable;
 import org.netbeans.lib.profiler.ui.swing.SearchUtils;
+import org.openide.util.Lookup;
 
 /**
  *
@@ -63,6 +68,19 @@ public final JMenuItem createCopyMenuItem() {
         return getResultsComponent().createCopyMenuItem();
     }
     
+    public final JMenuItem[] createCustomMenuItems(JComponent invoker, Object value, ClientUtils.SourceCodeSelection userValue) {
+        Collection<? extends PopupCustomizer> customizers = Lookup.getDefault().lookupAll(PopupCustomizer.class);
+        if (customizers.isEmpty()) return null;
+        
+        List<JMenuItem> menuItems = new ArrayList(customizers.size());
+        for (PopupCustomizer customizer : customizers) {
+            JMenuItem[] items = customizer.getMenuItems(invoker, this, value, userValue);
+            if (items != null) Collections.addAll(menuItems, items);
+        }
+        
+        return menuItems.isEmpty() ? null : menuItems.toArray(new JMenuItem[0]);
+    }
+    
     public void notifyOnFocus(final Runnable handler) {
         getResultsComponent().addFocusListener(new FocusAdapter() {
             public void focusGained(FocusEvent e) { handler.run(); }
@@ -266,4 +284,11 @@ public void layoutContainer(Container parent) {
         
     }
     
+    
+    public static abstract class PopupCustomizer {
+        
+        public abstract JMenuItem[] getMenuItems(JComponent invoker, DataView source, Object value, ClientUtils.SourceCodeSelection userValue);
+        
+    }
+    
 }
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerPopupMenu.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerPopupMenu.java
new file mode 100644
index 000000000..c2e176adf
--- /dev/null
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerPopupMenu.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.lib.profiler.ui.swing;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import javax.swing.JComponent;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.UIManager;
+import org.netbeans.lib.profiler.ui.UIUtils;
+
+/**
+ * JPopupMenu which supports custom background color.
+ *
+ * @author Jiri Sedlacek
+ */
+public class ProfilerPopupMenu extends JPopupMenu {
+    
+    private boolean forceBackground;
+    
+    
+    public ProfilerPopupMenu() {
+        super();
+    }
+
+    public ProfilerPopupMenu(String label) {
+        super(label);
+    }
+    
+    
+    // --- Tweaking UI ---------------------------------------------------------
+    
+    public JMenuItem add(JMenuItem menuItem) {
+        if (forceBackground && !UIUtils.isOracleLookAndFeel()) menuItem.setOpaque(false);
+        return super.add(menuItem);
+    }
+    
+    public void add(Component comp, Object constraints) {
+        if (forceBackground && !UIUtils.isOracleLookAndFeel() && comp instanceof JComponent)
+            ((JComponent)comp).setOpaque(false);
+        comp.setMinimumSize(comp.getPreferredSize());
+        super.add(comp, constraints);
+    }
+    
+    
+    public void setForceBackground(boolean force) {
+        if (!UIUtils.isNimbus() || !Boolean.TRUE.equals(UIManager.getBoolean("nb.dark.theme"))) // NOI18N
+            this.forceBackground = force;
+    }
+    
+    protected void paintComponent(Graphics g) {
+        super.paintComponent(g);
+        
+        if (forceBackground) {
+            g.setColor(getBackground());
+            g.fillRect(1, 1, getWidth() - 2, getHeight() - 2);
+        }
+    }
+    
+}
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerRowSorter.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerRowSorter.java
index 850d2b160..736378f92 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerRowSorter.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerRowSorter.java
@@ -24,6 +24,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Properties;
 import javax.swing.RowFilter;
 import javax.swing.RowSorter;
@@ -49,10 +50,50 @@
     private SortOrder defaultSortOrder = SortOrder.ASCENDING;
     private Map<Integer, SortOrder> defaultSortOrders;
     
-    private int secondarySortColumn;
+    private int secondarySortColumn = -1;
+    
+    private boolean threeStateColumns;
+    
+    public void setAllowsThreeStateColumns(boolean threeStateColumns) {
+        this.threeStateColumns = threeStateColumns;
+    }
+    
+    public boolean allowsThreeStateColumns() {
+        return threeStateColumns;
+    }
+    
+    public void toggleSortOrder(int column) {
+        // UNSORTED not allowed for sorting columns (default)
+        if (!allowsThreeStateColumns()) {
+            super.toggleSortOrder(column);
+            return;
+        }
+        
+        // Switching from one column to another
+        if (getSortColumn() != column) {
+            super.toggleSortOrder(column);
+            return;
+        }
+        
+        // Toggling from default sort order
+        SortOrder so = getSortOrder();
+        if (Objects.equals(getDefaultSortOrder(column), so)) {
+            super.toggleSortOrder(column);
+            return;
+        }
+        
+        // Resetting UNSORTED, use default sort order
+        if (Objects.equals(SortOrder.UNSORTED, so)) {
+            setSortColumn(column);
+            return;
+        }
+        
+        // Toggling from second sort order, switch to UNSORTED
+        setSortColumn(column, SortOrder.UNSORTED);
+    }
     
     public void setSortKeys(List newKeys) {
-        if (newKeys == null) {
+        if (newKeys == null || newKeys.isEmpty()) {
             setSortKeysImpl(newKeys);
             return;
         }
@@ -81,8 +122,9 @@ void setSortColumn(int column, SortOrder order) {
     }
     
     void setSortKey(RowSorter.SortKey key) {
-        RowSorter.SortKey secondaryKey = secondarySortColumn == key.getColumn() ?
-                          null : new RowSorter.SortKey(secondarySortColumn,
+        RowSorter.SortKey secondaryKey = secondarySortColumn == -1 ||
+                          secondarySortColumn == key.getColumn() ? null :
+                          new RowSorter.SortKey(secondarySortColumn,
                           getDefaultSortOrder(secondarySortColumn));
         setSortKeysImpl(secondaryKey == null ? Arrays.asList(key) :
                           Arrays.asList(key, secondaryKey));
@@ -100,7 +142,7 @@ SortOrder getSortOrder() {
     
     RowSorter.SortKey getSortKey() {
         List<? extends RowSorter.SortKey> keys = getSortKeys();
-        return keys.isEmpty() ? null : keys.get(0);
+        return keys == null || keys.isEmpty() ? null : keys.get(0);
     }
     
     
@@ -180,7 +222,7 @@ void loadFromStorage(Properties properties, ProfilerTable table) {
             try {
                 int column = Integer.parseInt(columnS);
                 SortOrder order = getSortOrder(orderS);
-                if (SortOrder.UNSORTED.equals(order)) order = getDefaultSortOrder(column);
+//                if (SortOrder.UNSORTED.equals(order)) order = getDefaultSortOrder(column);
                 setSortColumn(column, order);
             } catch (NumberFormatException e) {
                 // Reset sorting? Set default column?
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTable.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTable.java
index 3bdb6f5a5..331805b68 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTable.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTable.java
@@ -166,7 +166,7 @@ protected void setupAppearance() {
         getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
                 KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "DEFAULT_ACTION"); // NOI18N
         getActionMap().put("DEFAULT_ACTION", new AbstractAction() { // NOI18N
-                    public void actionPerformed(ActionEvent e) { performDefaultAction(); }
+                    public void actionPerformed(ActionEvent e) { performDefaultAction(e); }
                 });
         
         addFocusListener(new FocusListener() {
@@ -219,8 +219,7 @@ public Component prepareRenderer(TableCellRenderer renderer, int row, int column
             c.setBackground(UIManager.getColor("TextField.inactiveBackground")); // NOI18N
         } else {
             if (cEnabled) c.setForeground(getForeground());
-            c.setBackground((row & 0x1) == 0 ? getBackground() :
-                            UIUtils.getDarker(getBackground()));
+            c.setBackground(background(row, column));
         }
         
         c.move(0, 0);
@@ -234,6 +233,18 @@ public Component prepareRenderer(TableCellRenderer renderer, int row, int column
         }
     }
     
+    private Color background(int row, int column) {
+        Color background = (row & 0x1) == 0 ? getBackground() : UIUtils.getDarker(getBackground());
+//        if (convertColumnIndexToModel(column) == getSortColumn()) return UIUtils.getDarker(background);
+        if (convertColumnIndexToModel(column) == getSortColumn() && !SortOrder.UNSORTED.equals(_getRowSorter().getSortOrder())) {
+            int r = background.getRed() - 4;
+            int g = background.getGreen() - 4;
+            int b = background.getBlue() + 6;
+            background = UIUtils.getSafeColor(r, g, b);
+        }
+        return background;
+    }
+    
     public Component prepareEditor(TableCellEditor editor, int row, int column) {
         Component c = super.prepareEditor(editor, row, column);
         
@@ -403,6 +414,7 @@ public final int getMainColumn() {
     // --- Selection -----------------------------------------------------------
     
     private boolean shadeUnfocusedSelection = false;
+    private boolean selectionOnMiddlePress = false;
     
     boolean internal;
     private Object selection;
@@ -431,13 +443,22 @@ protected void saveSelection() {
     }
     
     protected void restoreSelection() {
-        if (!(selection instanceof Object[])) selection = selectValue(selection, mainColumn, false);
-        else selection = selectValues((Object[])selection, mainColumn, false);
+        try {
+            if (!(selection instanceof Object[])) selection = selectValue(selection, mainColumn, false);
+            else selection = selectValues((Object[])selection, mainColumn, false);
+        } catch (Exception e) {
+            System.err.println(">>> Exception in ProfilerTable.restoreSelection: " + e.getMessage());
+            e.printStackTrace();
+        }
     }
     
     public void selectRow(int row, boolean scrollToVisible) {
         internal = true;
         try { setRowSelectionInterval(row, row); saveSelection(); }
+        catch (Exception e) {
+            System.err.println(">>> Exception in ProfilerTable.selectRow: " + e.getMessage());
+            e.printStackTrace();
+        }
         finally { internal = false; }
         if (scrollToVisible) scrollRectToVisible(getCellRect(row, getSelectedColumn(), true));
     }
@@ -445,6 +466,10 @@ public void selectRow(int row, boolean scrollToVisible) {
     public void selectColumn(int column, boolean scrollToVisible) {
         internal = true;
         try { setColumnSelectionInterval(column, column); }
+        catch (Exception e) {
+            System.err.println(">>> Exception in ProfilerTable.selectColumn: " + e.getMessage());
+            e.printStackTrace();
+        }
         finally { internal = false; }
         if (scrollToVisible) scrollRectToVisible(getCellRect(getSelectedRow(), column, true));
     }
@@ -541,6 +566,14 @@ public final boolean shadesUnfocusedSelection() {
         return shadeUnfocusedSelection;
     }
     
+    public final void setSelectionOnMiddlePress(boolean select) {
+        selectionOnMiddlePress = select;
+    }
+    
+    public final boolean isSelectionOnMiddlePress() {
+        return selectionOnMiddlePress;
+    }
+    
     // --- Traversing rows -----------------------------------------------------
     
     int getNextRow(int row) {
@@ -616,7 +649,8 @@ protected void updateColumnsPreferredWidth() {
         
         ProfilerColumnModel cModel = _getColumnModel();
         
-        if (getRowCount() == 0) {
+        int rowCount = getRowCount();
+        if (rowCount == 0) {
             for (int column : scrollableColumns)
                 cModel.setColumnPreferredWidth(column, 0);
             return;
@@ -630,6 +664,8 @@ protected void updateColumnsPreferredWidth() {
         visibleP.translate(0, visible.height - 1);
         int last = rowAtPoint(visibleP);
         
+        if (last == -1) last = rowCount - 1; // last column above the visible rect boundary
+        
         for (int column : scrollableColumns) {
             int _column = convertColumnIndexToView(column);
             int width = computeColumnPreferredWidth(column, _column, first, last);
@@ -886,6 +922,14 @@ public void setDefaultSortOrder(int column, SortOrder sortOrder) {
         if (isSortable()) _getRowSorter().setDefaultSortOrder(column, sortOrder);
     }
     
+    public void setAllowsThreeStateColumns(boolean threeStateColumns) {
+        _getRowSorter().setAllowsThreeStateColumns(threeStateColumns);
+    }
+    
+    public boolean allowsThreeStateColumns() {
+        return _getRowSorter().allowsThreeStateColumns();
+    }
+    
     // --- Row filter ----------------------------------------------------------
     
     // false = OR, true = AND
@@ -921,7 +965,7 @@ public void setDefaultAction(Action action) {
         this.defaultAction = action;
     }
     
-    public void performDefaultAction() {
+    public void performDefaultAction(ActionEvent e) {
         if (defaultAction != null) defaultAction.actionPerformed(null);
     }
     
@@ -969,6 +1013,7 @@ protected void fireActionPerformed(ActionEvent e) {
             TableColumn column = columns.get(col);
             if (column.getWidth() > 0) {
                 String columnName = column.getHeaderValue().toString();
+                if (columnName.toLowerCase().startsWith("<html>")) columnName = columnName.replaceAll("<[^>]*>", ""); // NOI118N
                 copyItem.add(new JMenuItem(MessageFormat.format(genericItemName, columnName)) {
                     protected void fireActionPerformed(ActionEvent e) {
                         StringSelection s = new StringSelection(getStringValue(row, _col));
@@ -1030,11 +1075,21 @@ protected void processMouseEvent(MouseEvent e) {
         }
         
         // Right-press selects row for popup
-        if (popupEvent && e.getID() == MouseEvent.MOUSE_PRESSED && row != -1) {
-            ListSelectionModel sel = getSelectionModel();
-            if (sel.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION ||
-                !sel.isSelectedIndex(row)) selectRow(row, true);
+        // Middle-press selects if enabled
+        if (e.getID() == MouseEvent.MOUSE_PRESSED && row != -1) {
+            if (popupEvent || (selectionOnMiddlePress && SwingUtilities.isMiddleMouseButton(e))) {
+                ListSelectionModel sel = getSelectionModel();
+                if (sel.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION || !sel.isSelectedIndex(row)) {
+                    selectRow(row, true);
+                    requestFocusInWindow();
+                }
+            }
         }
+//        if (popupEvent && e.getID() == MouseEvent.MOUSE_PRESSED && row != -1) {
+//            ListSelectionModel sel = getSelectionModel();
+//            if (sel.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION ||
+//                !sel.isSelectedIndex(row)) selectRow(row, true);
+//        }
         
         super.processMouseEvent(e);
         
@@ -1050,7 +1105,8 @@ protected void processMouseEvent(MouseEvent e) {
         }
         
         // Only perform default action if not already processed (expand tree)
-        if (!e.isConsumed() && clickEvent && e.getClickCount() == 2) performDefaultAction();
+        if (!e.isConsumed() && clickEvent && e.getClickCount() == 2)
+            performDefaultAction(new ActionEvent(e.getSource(), e.getID(), "default action", e.getWhen(), e.getModifiers())); // NOI18N
         
         if (generatedClick != null) processMouseEvent(generatedClick);
     }
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTableContainer.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTableContainer.java
index 5fad468b4..8b89316c1 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTableContainer.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTableContainer.java
@@ -43,6 +43,7 @@
 import javax.swing.event.TableColumnModelEvent;
 import javax.swing.event.TableColumnModelListener;
 import javax.swing.table.JTableHeader;
+import org.netbeans.lib.profiler.ui.AppearanceController;
 import org.netbeans.lib.profiler.ui.UIUtils;
 
 /**
@@ -60,7 +61,7 @@
     public ProfilerTableContainer(final ProfilerTable table, boolean decorated,
                                   ColumnChangeAdapter adapter) {
         super(new BorderLayout());
-        setOpaque(false);
+        AppearanceController.getDefault().customizeProfilerTableContainer(this);
         
         this.table = table;
         
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTreeTable.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTreeTable.java
index 01b118acd..1e61b79ba 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTreeTable.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTreeTable.java
@@ -36,6 +36,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import javax.accessibility.Accessible;
 import javax.accessibility.AccessibleContext;
 import javax.swing.BorderFactory;
@@ -57,6 +58,7 @@
 import javax.swing.event.TreeModelListener;
 import javax.swing.event.TreeSelectionEvent;
 import javax.swing.event.TreeSelectionListener;
+import javax.swing.event.TreeWillExpandListener;
 import javax.swing.plaf.TreeUI;
 import javax.swing.plaf.basic.BasicTreeUI;
 import javax.swing.plaf.synth.SynthTreeUI;
@@ -67,6 +69,7 @@
 import javax.swing.table.TableRowSorter;
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.ExpandVetoException;
 import javax.swing.tree.TreeCellRenderer;
 import javax.swing.tree.TreeModel;
 import javax.swing.tree.TreeNode;
@@ -96,10 +99,10 @@ public ProfilerTreeTable(ProfilerTreeTableModel model, boolean sortable,
         this.model = (TableModelImpl)getModel();
         tree = this.model.getTree();
         
-        
         Adapter adapter = new Adapter();
         tree.addTreeSelectionListener(adapter);
         tree.addTreeExpansionListener(adapter);
+        tree.addTreeWillExpandListener(adapter);
         tree.getModel().addTreeModelListener(adapter);
         getSelectionModel().addListSelectionListener(adapter);
 
@@ -173,7 +176,7 @@ TreePath getPreviousPath(TreePath path, boolean down) { // TODO: optimize the al
         return path;
     }
     
-    void selectPath(TreePath path, boolean scrollToVisible) {
+    public void selectPath(TreePath path, boolean scrollToVisible) {
         internal = true;
         markExpansionTransaction();
         try {
@@ -216,10 +219,18 @@ public void setShowsRootHandles(boolean newValue) {
         if (tree != null) tree.setShowsRootHandles(newValue);
     }
     
+    public boolean getShowsRootHandles() {
+        return tree != null ? tree.getShowsRootHandles() : false;
+    }
+    
     public void setRootVisible(boolean rootVisible) {
         if (tree != null) tree.setRootVisible(rootVisible);
     }
     
+    public boolean isRootVisible() {
+        return tree != null ? tree.isRootVisible() : false;
+    }
+    
     
     private void markExpansionTransaction() {
         tree.putClientProperty(UIUtils.PROP_EXPANSION_TRANSACTION, Boolean.TRUE);
@@ -252,6 +263,27 @@ public void collapseChildren(int row) {
         }
     }
     
+    public void collapseChildren(TreePath tpath) {
+        if (tree != null) try {
+            markExpansionTransaction();
+            
+            if (tpath == null || tree.isCollapsed(tpath)) return;
+            
+            TreeModel tmodel = tree.getModel();
+            Object selected = tpath.getLastPathComponent();
+            
+            int nchildren = tmodel.getChildCount(selected);
+            for (int i = 0; i < nchildren; i++) {
+                TreePath tp = tpath.pathByAddingChild(tmodel.getChild(selected, i));
+                tree.collapsePath(tp);
+//                tree.resetExpandedState(tp);
+            }
+        
+        } finally {
+            clearExpansionTransaction();
+        }
+    }
+    
     public void collapseAll() {
         if (tree != null) try {
             markExpansionTransaction();
@@ -324,10 +356,81 @@ public void expandFirstPath(int row) {
         }
     }
     
+    public void expandPath(TreePath path) {
+        if (tree != null) try {
+            markExpansionTransaction();
+
+            tree.putClientProperty(UIUtils.PROP_AUTO_EXPANDING, Boolean.TRUE);
+            try { tree.expandPath(path); }
+            finally { tree.putClientProperty(UIUtils.PROP_AUTO_EXPANDING, null); }
+
+        } finally {
+            clearExpansionTransaction();
+        }
+    }
+    
+    public void expandRow(int row) {
+        if (tree != null) try {
+            markExpansionTransaction();
+
+            tree.putClientProperty(UIUtils.PROP_AUTO_EXPANDING, Boolean.TRUE);
+            try { tree.expandRow(row); }
+            finally { tree.putClientProperty(UIUtils.PROP_AUTO_EXPANDING, null); }
+
+        } finally {
+            clearExpansionTransaction();
+        }
+    }
+    
     public void makeTreeAutoExpandable(int maxChildToExpand) {
         if (tree != null) UIUtils.makeTreeAutoExpandable(tree, maxChildToExpand);
     }
     
+    protected void nodeExpanding(TreeNode node) {}
+    
+    protected void nodeExpanded(TreeNode node) {}
+    
+    protected void nodeCollapsing(TreeNode node) {}
+    
+    protected void nodeCollapsed(TreeNode node) {}
+    
+    public void setForgetPreviouslyExpanded(boolean ignorePreviouslyExpanded) {
+        tree.setForgetPreviouslyExpanded(ignorePreviouslyExpanded);
+    }
+    
+    public Enumeration<TreePath> getExpandedNodes() {
+        return tree == null ? null : tree.getExpandedDescendants(new TreePath(tree.getModel().getRoot()));
+    }
+    
+    public void clearExpandedNodes(Enumeration<TreePath> nodes) {
+        if (tree != null) {
+            tree.removeDescendantToggledPaths(nodes);
+            tree.updateUI();
+        }
+    }
+    
+    public void resetExpandedNodes() {
+        if (tree != null) tree.resetExpandedNodes();
+    }
+    
+    public void resetPath(TreePath path) {
+        if (tree != null) ((SortedFilteredTreeModel)tree.getModel()).clearPath(path);
+    }
+    
+    public DefaultTreeModel getTreeModel() {
+        return tree == null ? null : (DefaultTreeModel)tree.getModel();
+    }
+    
+//    public void resetTreeUI() {
+//        tree.updateUI();
+//    }
+    
+    public static abstract class NodeExpansionEvaluator {
+        
+        public abstract Boolean hasBeenExpanded(TreePath path);
+        
+    }
+    
     
     public void setCellRenderer(TreeCellRenderer renderer) {
         if (tree != null) {
@@ -400,7 +503,7 @@ protected void processMouseEvent(MouseEvent e) {
             Point point = e.getPoint();
             int column = columnAtPoint(point);
             
-            if (getColumnClass(column) == JTree.class) {
+            if (column != -1 && getColumnClass(column) == JTree.class) {
                 int row = rowAtPoint(point);
                 Rectangle treeCellRect = tree.getRowBounds(row);
                 
@@ -445,6 +548,11 @@ private void refreshFilter() {
     }
     
     
+    public Comparator getCurrentComparator() {
+        return model.getComparator();
+    }
+    
+    
     // --- String value --------------------------------------------------------
     
     // column - view index
@@ -474,13 +582,20 @@ public void allRowsChanged() {
                     public void run() { updateColumnsPreferredWidth(); }
                 });
             }
+            protected void setSortKeysImpl(List newKeys) {
+                // TODO: Improve to not call createComparator(newKeys) here and from super
+                willBeSorted(Collections.unmodifiableList(newKeys));
+                super.setSortKeysImpl(newKeys);
+            }
         };
         s.setDefaultSortOrder(SortOrder.DESCENDING);
         s.setDefaultSortOrder(0, SortOrder.ASCENDING);
-        s.setSortColumn(0);
+//        s.setSortColumn(0);
         return s;
     }
     
+    protected void willBeSorted(List<? extends RowSorter.SortKey> sortKeys) {}
+    
     private static class ProfilerTreeTableSorter extends ProfilerRowSorter {
         
         private final TableModelImpl model;
@@ -522,33 +637,58 @@ public RowFilter getRowFilter() {
         protected void setSortKeysImpl(List newKeys) {
             sortKeys = newKeys == null ? Collections.emptyList() :
                        Collections.unmodifiableList(new ArrayList(newKeys));
-            model.sort(newKeys == null ? null : getComparator());
+//            long start = System.currentTimeMillis();
+            model.sort(createComparator(newKeys));
+//            System.err.println(">>> Sorted in " + (System.currentTimeMillis() - start));
+//            Thread.dumpStack();
         }
         
         public List<? extends RowSorter.SortKey> getSortKeys() {
             return sortKeys;
         }
         
-        private Comparator getComparator() {
-            SortOrder sortOrder = getSortOrder();
+        protected Comparator createComparator(List<RowSorter.SortKey> sortKeys) {
+            if (sortKeys == null || sortKeys.isEmpty()) return null;
+            
+            SortKey sortKey = sortKeys.get(0);
+            SortOrder sortOrder = sortKey.getSortOrder();
             if (SortOrder.UNSORTED.equals(sortOrder)) return null;
             
             final boolean ascending = SortOrder.ASCENDING.equals(sortOrder);
-            final int sortColumn = getSortColumn();
-            boolean sortingTree = JTree.class.equals(model.getColumnClass(sortColumn));
-            final Comparator comparator = sortingTree ? null : getComparator(sortColumn);
+            final int sortColumn = sortKey.getColumn();
+            
+            Class columnClass = model.getColumnClass(sortColumn);
+            final Comparator comp = JTree.class.equals(columnClass) ? null : 
+                    (Comparable.class.isAssignableFrom(columnClass) ? new Comparator() {
+                        public int compare(Object o1, Object o2) {
+                            return ((Comparable)o1).compareTo(o2);
+                        }
+                    } : getComparator(sortColumn));
+            
+//            System.err.println(">>> ascending: " + ascending);
+//            System.err.println(">>> sortColumn: " + sortColumn);
+//            System.err.println(">>> columnClass: " + columnClass + " - name: " + model.getColumnName(sortColumn));
             
             return new Comparator() {
                 public int compare(Object o1, Object o2) {
                     int result;
-                    if (comparator == null) {
-                        String s1 = o1.toString();
-                        String s2 = o2.toString();
-                        result = s1.compareTo(s2);
+                    
+                    if (o1 instanceof CCTNode.FixedPosition) {
+                        result = o1 instanceof CCTNode.AlwaysFirst ? -1 : 1;
+                        result = ascending ? result : -result;
+                    } else if (o2 instanceof CCTNode.FixedPosition) {
+                        result = o2 instanceof CCTNode.AlwaysFirst ? 1 : -1;
+                        result = ascending ? result : -result;
+                    } else if (comp == null) {
+                        result = o1.toString().compareTo(o2.toString());
                     } else {
                         Object v1 = model.getValueAt((TreeNode)o1, sortColumn);
                         Object v2 = model.getValueAt((TreeNode)o2, sortColumn);
-                        result = comparator.compare(v1, v2);
+                        
+                        if (v1 == v2) result = 0;
+                        else if (v1 == null) result = -1;
+                        else if (v2 == null) result = 1;
+                        else result = comp.compare(v1, v2);
                     }
                     
                     return ascending ? result : result * -1;
@@ -556,6 +696,45 @@ public int compare(Object o1, Object o2) {
             };
         }
         
+//        private Comparator getComparator() {
+//            SortOrder sortOrder = getSortOrder();
+//            if (SortOrder.UNSORTED.equals(sortOrder)) return null;
+//            
+//            final boolean ascending = SortOrder.ASCENDING.equals(sortOrder);
+//            final int sortColumn = getSortColumn();
+//            Class columnClass = model.getColumnClass(sortColumn);
+//            final boolean sortingTree = JTree.class.equals(columnClass);
+//            final Comparator comparator = sortingTree ? null : 
+//                    (Comparable.class.isAssignableFrom(columnClass) ? new Comparator() {
+//                        public int compare(Object o1, Object o2) {
+//                            return ((Comparable)o1).compareTo(o2);
+//                        }
+//                    } : getComparator(sortColumn));
+////            Class columnClass = model.getColumnClass(sortColumn);
+////            boolean sortingStrings = JTree.class.equals(columnClass) || String.class.equals(columnClass);
+////            final Comparator comparator = sortingStrings ? null : getComparator(sortColumn);
+//            
+//            return new Comparator() {
+//                public int compare(Object o1, Object o2) {
+//                    int result;
+//                    
+//                    if (sortingTree) {
+//                        result = o1.toString().compareTo(o2.toString());
+//                    } else {
+//                        Object v1 = model.getValueAt((TreeNode)o1, sortColumn);
+//                        Object v2 = model.getValueAt((TreeNode)o2, sortColumn);
+//                        
+//                        if (v1 == v2) result = 0;
+//                        else if (v1 == null) result = -1;
+//                        else if (v2 == null) result = 1;
+//                        else result = comparator.compare(v1, v2);
+//                    }
+//                    
+//                    return ascending ? result : result * -1;
+//                }
+//            };
+//        }
+        
     }
     
     
@@ -565,7 +744,7 @@ public int compare(Object o1, Object o2) {
         private SortedFilteredTreeModel treeModel;
         private final ProfilerTreeTableModel treeTableModel;
         
-        TableModelImpl(ProfilerTreeTableModel model) {
+        TableModelImpl(final ProfilerTreeTableModel model) {
             this.treeTableModel = model;
             
             treeModel = treeModelImpl(model.getRoot(), null, null);
@@ -574,6 +753,16 @@ public int compare(Object o1, Object o2) {
                 public void dataChanged() {
                     fireTableDataChanged();
                 }
+                public void structureChanged() {
+                    treeModel.reload();
+//                    fireTableDataChanged();
+                }
+                public void childrenChanged(TreeNode node) {
+//                    System.err.println(">>>      Firing nodeStructureChanged to the tree...");
+                    treeModel.reload(node);
+//                    System.err.println(">>>      Fired nodeStructureChanged to the tree.");
+//                    fireTableDataChanged();
+                }
                 public void rootChanged(TreeNode oldRoot, TreeNode newRoot) {
                     // NOTE: would be cleaner to change root of existing model,
                     //       wasn't able to easily resolve all related problems.
@@ -619,6 +808,10 @@ void sort(Comparator comparator) {
             treeModel.setComparator(comparator);
         }
         
+        Comparator getComparator() {
+            return treeModel.getComparator();
+        }
+        
         void filter(RowFilter filter) {
             treeModel.setFilter(filter);
         }
@@ -726,6 +919,27 @@ public int getChildCount(Object parent) {
         }
         
         
+        void clearPath(TreePath path) {
+            clearKey(null);
+//            clearKey(new TreePathKey(getPath(path)));
+        }
+        
+//        private TreeNode[] getPath(TreePath path) {
+//            int i = path.getPathCount();
+//            TreeNode[] result = new TreeNode[i--];
+//
+//            for(TreePath tp = path; tp != null; tp = tp.getParentPath())
+//                result[i--] = (TreeNode)tp.getLastPathComponent();
+//            
+//            return result;
+//        }
+        
+        protected void clearKey(TreePathKey key) {
+            cache = null;
+//            if (cache != null) cache.remove(key);
+        }
+        
+        
         protected void fireTreeStructureChanged(Object source, Object[] path,
                                                 int[] childIndices,
                                                 Object[] children) {
@@ -836,10 +1050,18 @@ public int getIndexOfChild(Object parent, Object child) {
         }
         
         
+        protected void clearKey(TreePathKey key) {
+            super.clearKey(key);
+            viewToModel = null;
+//            if (viewToModel != null) viewToModel.remove(key);
+        }
+        
+        
         protected void fireTreeStructureChanged(Object source, Object[] path,
                                                 int[] childIndices,
                                                 Object[] children) {
             viewToModel = null;
+//            System.err.println(">>> CLEARING MODEL <<<");
             super.fireTreeStructureChanged(source, path, childIndices, children);
         }
         
@@ -869,11 +1091,16 @@ protected void fireTreeStructureChanged(Object source, Object[] path,
     private static final class TreePathKey {
         
         private final TreeNode[] pathToRoot;
-        private final int hashCode;
+        private int hashCode;
 
         TreePathKey(TreeNode[] _pathToRoot) {
             pathToRoot = _pathToRoot;
-            hashCode = Arrays.deepHashCode(pathToRoot);
+            
+            hashCode = 1;
+            for (TreeNode node : pathToRoot)
+                hashCode = 31 * hashCode + node.hashCode();
+            
+//            hashCode = Arrays.deepHashCode(pathToRoot);
         }
 
         public final int hashCode() {
@@ -910,19 +1137,80 @@ static void restoreExpandedNodes(JTree tree, UIState uiState) {
         try {
             tree.putClientProperty(UIUtils.PROP_EXPANSION_TRANSACTION, Boolean.TRUE);
             Enumeration<TreePath> paths = uiState.getExpandedPaths();
-            if (paths != null) while (paths.hasMoreElements())
-                tree.expandPath(paths.nextElement());
+            if (paths != null) while (paths.hasMoreElements()) {
+                TreePath tp = paths.nextElement();
+//                System.err.println(">>> Restoring expanded " + tp);
+                tree.expandPath(getSimilarPath(tp, tree.getModel()));
+            }
+        } catch (Exception e) {
+            System.err.println(">>> Exception in ProfilerTreeTable.restoreExpandedNodes: " + e.getMessage());
+            e.printStackTrace();
         } finally {
             tree.putClientProperty(UIUtils.PROP_EXPANSION_TRANSACTION, null);
         }
     }
     
     static void restoreSelectedNodes(JTree tree, UIState uiState) {
-        tree.setSelectionPaths(uiState.getSelectedPaths());
+        try {
+            TreePath[] sel = uiState.getSelectedPaths();
+
+            if (sel != null)
+                for (int i = 0; i < sel.length; i++)
+                    sel[i] = getSimilarPath(sel[i], tree.getModel());
+            tree.setSelectionPaths(sel);
+        } catch (Exception e) {
+            System.err.println(">>> Exception in ProfilerTreeTable.restoreSelectedNodes: " + e.getMessage());
+            e.printStackTrace();
+        }
     }
     
     
-    static class UIState {
+    protected UIState getUIState() {
+        return tree == null ? null : getUIState(tree);
+    }
+    
+    protected void restoreExpandedNodes(UIState uiState) {
+        if (tree != null) restoreExpandedNodes(tree, uiState);
+    }
+    
+    protected void restoreSelectedNodes(UIState uiState) {
+        if (tree != null) restoreSelectedNodes(tree, uiState);
+    }
+    
+    
+    private static TreePath getSimilarPath(TreePath oldPath, TreeModel currentModel) {
+        if (oldPath == null || oldPath.getPathCount() < 1) return null;
+
+//        TreeModel currentModel = getModel();
+        Object currentRoot = currentModel.getRoot();
+        if (!currentRoot.equals(oldPath.getPathComponent(0))) return null;
+
+        TreePath p = new TreePath(currentRoot);
+        Object[] op = oldPath.getPath();
+        Object n = currentRoot;
+
+        for (int i = 1; i < op.length; i++) {
+            Object nn = null;
+
+            for (int ii = 0; ii < currentModel.getChildCount(n); ii++) {
+                Object c = currentModel.getChild(n, ii);
+                if (c.equals(op[i])) {
+                    nn = c;
+                    break;
+                }
+            }
+
+            if (nn == null) return null;
+
+            n = nn;
+            p = p.pathByAddingChild(n);
+        }
+//        System.err.println(">>> Similar path for " + oldPath + " is " + p);
+        return p;
+    }
+    
+    
+    protected static class UIState {
         
         private final TreePath[] selectedPaths;
         private final Enumeration<TreePath> expandedPaths;
@@ -945,14 +1233,30 @@ public Enumeration getExpandedPaths() {
     }
     
     
-    private class Adapter implements TreeModelListener, TreeExpansionListener,
+    private class Adapter implements TreeModelListener, TreeExpansionListener, TreeWillExpandListener,
                                      TreeSelectionListener, ListSelectionListener {
         
         private boolean internal;
         
-        public void treeExpanded(TreeExpansionEvent event) { notifyTable(); }
-
-        public void treeCollapsed(TreeExpansionEvent event) { notifyTable(); }
+        public void treeExpanded(TreeExpansionEvent event) {
+            notifyTable();
+            Object expanded = event.getPath().getLastPathComponent();
+            if (expanded instanceof TreeNode) nodeExpanded((TreeNode)expanded);
+        }
+        public void treeCollapsed(final TreeExpansionEvent event) {
+            notifyTable();
+            Object collapsed = event.getPath().getLastPathComponent();
+            if (collapsed instanceof TreeNode) nodeCollapsed((TreeNode)collapsed);
+        }
+        
+        public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
+            Object expanding = event.getPath().getLastPathComponent();
+            if (expanding instanceof TreeNode) nodeExpanding((TreeNode)expanding);
+        }
+        public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
+            Object collapsing = event.getPath().getLastPathComponent();
+            if (collapsing instanceof TreeNode) nodeCollapsing((TreeNode)collapsing);
+        }
 
         public void treeNodesChanged(TreeModelEvent e) { notifyTable(); }
 
@@ -960,7 +1264,11 @@ public Enumeration getExpandedPaths() {
 
         public void treeNodesRemoved(TreeModelEvent e) { notifyTable(); }
 
-        public void treeStructureChanged(TreeModelEvent e) { notifyTable(); }
+        public void treeStructureChanged(TreeModelEvent e) {
+            tree.setChangingModel(true);
+            try { notifyTable(); }
+            finally { tree.setChangingModel(false); }
+        }
         
         private void notifyTable() {
             if (tree.isChangingModel()) return;
@@ -975,6 +1283,12 @@ public void valueChanged(TreeSelectionEvent e) {
             if (internal) return;
             
             TreePath selected = e.getPath();
+            
+            // NOTE: workaround to not break scrollRectToVisible() when there's
+            //       an expanded node above the selection.
+            //       Can be detected by e.getPath() != tree.getSelectionPath().
+            if (!Objects.equals(selected, tree.getSelectionPath())) return;
+            
             int row = selected == null ? -1 : tree.getRowForPath(selected);
             try {
                 internal = true;
@@ -1016,6 +1330,8 @@ public void valueChanged(ListSelectionEvent e) {
         
         private SynthLikeTreeUI synthLikeUI;
         private boolean workaroundVerticalLines;
+        
+        private boolean forgetPreviouslyExpanded;
 
         
         ProfilerTreeTableTree(SortedFilteredTreeModel model) {
@@ -1032,7 +1348,7 @@ public void valueChanged(ListSelectionEvent e) {
         
         public void setUI(TreeUI ui) {
             if (ui instanceof SynthTreeUI) {
-                if (synthLikeUI == null) {
+//                if (synthLikeUI == null) {
                     super.setUI(ui);
                     SynthTreeUI synthUI = (SynthTreeUI)ui;
                     int left = synthUI.getLeftChildIndent();
@@ -1044,10 +1360,12 @@ public void setUI(TreeUI ui) {
                     boolean nimbus = UIUtils.isNimbusLookAndFeel();
                     synthLikeUI.setLeftChildIndent(left + (nimbus ? 4 : 6));
                     synthLikeUI.setRightChildIndent(right);
-                } else {
-                    super.setUI(synthLikeUI);
-                }
+//                } else {
+//                    super.setUI(synthLikeUI);
+//                }
             } else {
+                synthLikeUI = null;
+                
                 super.setUI(ui);
                 
                 // #269500 - performance workaround for BasicTreeUI
@@ -1077,8 +1395,13 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
             currentSelected = isSelected;
             
             Rectangle cellBounds = getRowBounds(row);
-            currentX = cellBounds.x;
-            currentWidth = cellBounds.width;
+            if (cellBounds == null) { // row may be -1
+                currentX = 0;
+                currentWidth = 0;
+            } else {
+                currentX = cellBounds.x;
+                currentWidth = cellBounds.width;
+            }
             
             customRendering = ptable.isCustomRendering();
             if (synthLikeUI != null) synthLikeUI.setSelected(isSelected);
@@ -1115,6 +1438,37 @@ private boolean isFirstColumn(TableColumnModel columns, int column) {
             return x == 0;
         }
         
+        
+        public void setAnchorSelectionPath(TreePath newPath) {
+            // TODO: should only be disabled for forgetPreviouslyExpanded?
+        }
+        
+        void setForgetPreviouslyExpanded(boolean forgetPreviouslyExpanded) {
+            this.forgetPreviouslyExpanded = forgetPreviouslyExpanded;
+        }
+        
+        public boolean hasBeenExpanded(TreePath path) {
+            return forgetPreviouslyExpanded ? false : super.hasBeenExpanded(path);
+        }
+        
+        public void fireTreeCollapsed(TreePath path) {
+            super.fireTreeCollapsed(path);
+            if (forgetPreviouslyExpanded) {
+                super.removeDescendantToggledPaths(Collections.enumeration(Collections.singletonList(path)));
+                // NOTE: uncachePath() must be called for all DescendantToggledPaths once implemented!
+                ((SortedFilteredTreeModel)getModel()).clearPath(path);
+                SwingUtilities.invokeLater(new Runnable() {
+                    public void run() { updateUI(); }
+                });
+            }
+        }
+        
+        protected void removeDescendantToggledPaths(Enumeration<TreePath> toRemove) {
+//            System.err.println(">>> REMOVING descendant toggled paths...");
+//            Thread.dumpStack();
+            super.removeDescendantToggledPaths(toRemove);
+        }
+        
         void resetExpandedNodes() {
             clearToggledPaths();
             updateUI();
@@ -1136,10 +1490,16 @@ public void setSelectionPath(TreePath path) {
         }
         
         public void setSelectionPaths(TreePath[] paths) {
-            if (changingModel && paths != null)
-                for (int i = 0; i < paths.length; i++)
-                    paths[i] = getSimilarPath(paths[i]);
-            super.setSelectionPaths(paths);
+            if (changingModel && paths != null) {
+                List<TreePath> similarPaths = new ArrayList();
+                for (int i = 0; i < paths.length; i++) {
+                    TreePath similarPath = getSimilarPath(paths[i]);
+                    if (similarPath != null) similarPaths.add(similarPath);
+                }
+                super.setSelectionPaths(similarPaths.toArray(new TreePath[0]));
+            } else {
+                super.setSelectionPaths(paths);
+            }
         }
         
         private TreePath getSimilarPath(TreePath oldPath) {
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTreeTableModel.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTreeTableModel.java
index 8acb10a0b..50cbf019b 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTreeTableModel.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/ProfilerTreeTableModel.java
@@ -63,6 +63,14 @@ public void dataChanged() {
             fireDataChanged();
         }
         
+        public void structureChanged() {
+            fireStructureChanged();
+        }
+        
+        public void childrenChanged(TreeNode node) {
+            fireChildrenChanged(node);
+        }
+        
         public void setRoot(TreeNode newRoot) {
             TreeNode oldRoot = root;
             root = newRoot;
@@ -97,6 +105,12 @@ protected void fireStructureChanged() {
                     listener.structureChanged();
         }
         
+        protected void fireChildrenChanged(TreeNode node) {
+            if (listeners != null)
+                for (Listener listener : listeners)
+                    listener.childrenChanged(node);
+        }
+        
         protected void fireRootChanged(TreeNode oldRoot, TreeNode newRoot) {
             if (listeners != null)
                 for (Listener listener : listeners)
@@ -112,6 +126,8 @@ protected void fireRootChanged(TreeNode oldRoot, TreeNode newRoot) {
         
         public void structureChanged();
         
+        public void childrenChanged(TreeNode node);
+        
         public void rootChanged(TreeNode oldRoot, TreeNode newRoot);
         
     }
@@ -122,6 +138,8 @@ public void dataChanged() {}
         
         public void structureChanged() {}
         
+        public void childrenChanged(TreeNode node) {}
+        
         public void rootChanged(TreeNode oldRoot, TreeNode newRoot) {}
         
     }
diff --git a/profiler/src/org/netbeans/modules/profiler/v2/ui/StayOpenPopupMenu.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/StayOpenPopupMenu.java
similarity index 85%
rename from profiler/src/org/netbeans/modules/profiler/v2/ui/StayOpenPopupMenu.java
rename to lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/StayOpenPopupMenu.java
index 434c62169..010d57b0d 100644
--- a/profiler/src/org/netbeans/modules/profiler/v2/ui/StayOpenPopupMenu.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/StayOpenPopupMenu.java
@@ -17,11 +17,10 @@
  * under the License.
  */
 
-package org.netbeans.modules.profiler.v2.ui;
+package org.netbeans.lib.profiler.ui.swing;
 
 import java.awt.Component;
 import java.awt.EventQueue;
-import java.awt.Graphics;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.KeyEvent;
@@ -29,16 +28,12 @@
 import javax.swing.Action;
 import javax.swing.Icon;
 import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JComponent;
 import javax.swing.JMenuItem;
-import javax.swing.JPopupMenu;
 import javax.swing.JRadioButtonMenuItem;
 import javax.swing.JToggleButton;
 import javax.swing.KeyStroke;
 import javax.swing.MenuElement;
 import javax.swing.MenuSelectionManager;
-import javax.swing.UIManager;
-import org.netbeans.lib.profiler.ui.UIUtils;
 
 /**
  * A JPopupMenu implementation optionally allowing to interact with JMenuItems
@@ -49,9 +44,7 @@
  *
  * @author Jiri Sedlacek
  */
-public class StayOpenPopupMenu extends JPopupMenu {
-    
-    private boolean forceBackground;
+public class StayOpenPopupMenu extends ProfilerPopupMenu {
     
     
     public StayOpenPopupMenu() {
@@ -63,36 +56,6 @@ public StayOpenPopupMenu(String label) {
     }
     
     
-    // --- Tweaking UI ---------------------------------------------------------
-    
-    public JMenuItem add(JMenuItem menuItem) {
-        if (forceBackground && !UIUtils.isOracleLookAndFeel()) menuItem.setOpaque(false);
-        return super.add(menuItem);
-    }
-    
-    public void add(Component comp, Object constraints) {
-        if (forceBackground && !UIUtils.isOracleLookAndFeel() && comp instanceof JComponent)
-            ((JComponent)comp).setOpaque(false);
-        comp.setMinimumSize(comp.getPreferredSize());
-        super.add(comp, constraints);
-    }
-    
-    
-    public void setForceBackground(boolean force) {
-        if (!UIUtils.isNimbus() || !Boolean.TRUE.equals(UIManager.getBoolean("nb.dark.theme"))) // NOI18N
-            this.forceBackground = force;
-    }
-    
-    protected void paintComponent(Graphics g) {
-        super.paintComponent(g);
-        
-        if (forceBackground) {
-            g.setColor(getBackground());
-            g.fillRect(1, 1, getWidth() - 2, getHeight() - 2);
-        }
-    }
-    
-    
     // --- Handling keyboard events --------------------------------------------
     
     private static boolean isReturnAction(KeyEvent e) {
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/HideableBarRenderer.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/HideableBarRenderer.java
index 6f18ec1b6..872ddeb40 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/HideableBarRenderer.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/HideableBarRenderer.java
@@ -122,11 +122,13 @@ public void paint(Graphics g) {
         component.setSize(componentWidth, size.height);
         component.paint(g);
         
-        int freeWidth = size.width - maxRendererWidth - renderersGap();
-        if (freeWidth >= MIN_BAR_WIDTH) {
-            barRenderer.setSize(Math.min(freeWidth, MAX_BAR_WIDTH), size.height);
-            barRenderer.move(location.x, location.y);
-            barRenderer.paint(g);
+        if (numberPercentRenderer == null || numberPercentRenderer.valueRenderers()[1].getComponent().isVisible()) {
+            int freeWidth = size.width - maxRendererWidth - renderersGap();
+            if (freeWidth >= MIN_BAR_WIDTH) {
+                barRenderer.setSize(Math.min(freeWidth, MAX_BAR_WIDTH), size.height);
+                barRenderer.move(location.x, location.y);
+                barRenderer.paint(g);
+            }
         }
     }
     
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/MultiRenderer.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/MultiRenderer.java
index e18790b9e..1074e21d9 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/MultiRenderer.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/MultiRenderer.java
@@ -155,4 +155,11 @@ public void paint(Graphics g) {
         }
     }
     
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        ProfilerRenderer[] renderers = valueRenderers();
+        if (renderers != null) for (ProfilerRenderer renderer : renderers) sb.append(renderer.toString());
+        return sb.toString();
+    }
+    
 }
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/NormalBoldGrayRenderer.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/NormalBoldGrayRenderer.java
index 11af31504..f777bd5fe 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/NormalBoldGrayRenderer.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/swing/renderer/NormalBoldGrayRenderer.java
@@ -141,9 +141,31 @@ protected void setIcon(Icon icon) {
         }
     }
     
-    
-    public String toString() {
-        return normalRenderer.toString() + boldRenderer.toString() + grayRenderer.toString();
+    public Icon getIcon() {
+        Icon icon = normalRenderer.getIcon();
+        if (icon == null) icon = boldRenderer.getIcon();
+        if (icon == null) icon = grayRenderer.getIcon();
+        return icon;
     }
     
+    // Invoke after values are set!
+    protected void setIconTextGap(int gap) {
+        String text = normalRenderer.getText();
+        if (text == null || text.isEmpty()) {
+//            normalRenderer.setIcon(null);
+            text = boldRenderer.getText();
+            if (text == null || text.isEmpty()) {
+//                boldRenderer.setIcon(null);
+                grayRenderer.setIconTextGap(gap);
+            } else {
+                boldRenderer.setIconTextGap(gap);
+//                grayRenderer.setIcon(null);
+            }
+        } else {
+            normalRenderer.setIconTextGap(gap);
+//            boldRenderer.setIcon(null);
+//            grayRenderer.setIcon(null);
+        }
+    }
+        
 }
diff --git a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/threads/ThreadsPanel.java b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/threads/ThreadsPanel.java
index 0ac9b6bb4..1e2848fc9 100644
--- a/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/threads/ThreadsPanel.java
+++ b/lib.profiler.ui/src/org/netbeans/lib/profiler/ui/threads/ThreadsPanel.java
@@ -50,6 +50,7 @@
 import org.netbeans.lib.profiler.results.DataManagerListener;
 import org.netbeans.lib.profiler.results.threads.ThreadData;
 import org.netbeans.lib.profiler.results.threads.ThreadsDataManager;
+import org.netbeans.lib.profiler.ui.AppearanceController;
 import org.netbeans.lib.profiler.ui.Formatters;
 import org.netbeans.lib.profiler.ui.UIUtils;
 import org.netbeans.lib.profiler.ui.results.DataView;
@@ -149,6 +150,7 @@ public void cleanup() {
     
     
     private void initUI(Action saveView) {
+        AppearanceController.getDefault().customizeThreadPanel(this);
         
         final AbstractTableModel threadsTableModel = new AbstractTableModel() {
             public String getColumnName(int columnIndex) {
diff --git a/lib.profiler/manifest.mf b/lib.profiler/manifest.mf
index a30b6a6b2..f0da61020 100644
--- a/lib.profiler/manifest.mf
+++ b/lib.profiler/manifest.mf
@@ -1,5 +1,5 @@
 Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.lib.profiler/1
 OpenIDE-Module-Localizing-Bundle: org/netbeans/lib/profiler/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.103
+OpenIDE-Module-Specification-Version: 1.109
 
diff --git a/lib.profiler/nbproject/project.xml b/lib.profiler/nbproject/project.xml
index 51c2c9ae1..6c23344ef 100644
--- a/lib.profiler/nbproject/project.xml
+++ b/lib.profiler/nbproject/project.xml
@@ -55,6 +55,10 @@
                 <friend>com.sun.tools.visualvm.application.views</friend>
                 <friend>com.sun.tools.visualvm.core</friend>
                 <friend>com.sun.tools.visualvm.heapdump</friend>
+                <friend>com.sun.tools.visualvm.heapviewer</friend>
+                <friend>com.sun.tools.visualvm.heapviewer.console</friend>
+                <friend>com.sun.tools.visualvm.heapviewer.truffle</friend>
+                <friend>com.sun.tools.visualvm.modules.appui</friend>
                 <friend>com.sun.tools.visualvm.profiler</friend>
                 <friend>com.sun.tools.visualvm.profiling</friend>
                 <friend>com.sun.tools.visualvm.sampler</friend>
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/ProfilerClient.java b/lib.profiler/src/org/netbeans/lib/profiler/ProfilerClient.java
index 202d8a931..4d18f01e5 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/ProfilerClient.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/ProfilerClient.java
@@ -1568,6 +1568,7 @@ private boolean setVMProperties(VMPropertiesResponse resp, boolean terminateOnEr
         status.maxHeapSize = resp.getMaxHeapSize();
         status.startupTimeMillis = resp.getStartupTimeMillis();
         status.startupTimeInCounts = resp.getStartupTimeInCounts();
+        status.canInstrumentConstructor = resp.canInstrumentConstructor();
 
         if (!status.remoteProfiling && settings.getTargetJDKVersionString() != CommonConstants.JDK_19_STRING) {
             settings.setWorkingDir(resp.getWorkingDir());
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/global/CommonConstants.java b/lib.profiler/src/org/netbeans/lib/profiler/global/CommonConstants.java
index 3b1419adf..ab4559718 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/global/CommonConstants.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/global/CommonConstants.java
@@ -285,5 +285,6 @@
     public static final int AGENT_VERSION_80 = 15;
     public static final int AGENT_VERSION_81 = 16;
     public static final int AGENT_VERSION_82 = 17;
-    public static final int CURRENT_AGENT_VERSION = AGENT_VERSION_82;
+    public static final int AGENT_VERSION_90 = 18;
+    public static final int CURRENT_AGENT_VERSION = AGENT_VERSION_90;
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/global/ProfilingSessionStatus.java b/lib.profiler/src/org/netbeans/lib/profiler/global/ProfilingSessionStatus.java
index 5a798ab1d..80284a10b 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/global/ProfilingSessionStatus.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/global/ProfilingSessionStatus.java
@@ -62,6 +62,9 @@
     // Target machine OS, as returned by System.getProperty("os.name")
     public String targetMachineOSName;
 
+    // true if target JDK class file verifier accepts our constructor instrumentation
+    public boolean canInstrumentConstructor;
+
     // Important timer-related characteristics of instrumentation that we inject into the TA.
     // Each array has 5 elements:
     // o value when absolute timer is used;
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/AbstractLongMap.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/AbstractLongMap.java
index 7fee68d6e..5deaf77ae 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/AbstractLongMap.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/AbstractLongMap.java
@@ -19,8 +19,11 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.nio.MappedByteBuffer;
@@ -47,10 +50,11 @@
     final int ID_SIZE;
     final int FOFFSET_SIZE;
     Data dumpBuffer;
+    CacheDirectory cacheDirectory;
     
     //~ Constructors -------------------------------------------------------------------------------------------------------------
 
-    AbstractLongMap(int size,int idSize,int foffsetSize,int valueSize) throws FileNotFoundException, IOException {
+    AbstractLongMap(int size,int idSize,int foffsetSize,int valueSize,CacheDirectory cacheDir) throws FileNotFoundException, IOException {
         assert idSize == 4 || idSize == 8;
         assert foffsetSize == 4 || foffsetSize == 8;
         keys = (size * 4L) / 3L;
@@ -60,7 +64,7 @@
         VALUE_SIZE = valueSize;
         ENTRY_SIZE = KEY_SIZE + VALUE_SIZE;
         fileSize = keys * ENTRY_SIZE;
-        tempFile = File.createTempFile("NBProfiler", ".map"); // NOI18N
+        tempFile = cacheDir.createTempFile("NBProfiler", ".map"); // NOI18N
 
         RandomAccessFile file = new RandomAccessFile(tempFile, "rw"); // NOI18N
         if (Boolean.getBoolean("org.netbeans.lib.profiler.heap.zerofile")) {    // NOI18N
@@ -72,13 +76,15 @@
         }
         file.setLength(fileSize);
         setDumpBuffer(file);
-        tempFile.deleteOnExit();
+        cacheDirectory = cacheDir;
     }
 
     //~ Methods ------------------------------------------------------------------------------------------------------------------
 
     protected void finalize() throws Throwable {
-        tempFile.delete();
+        if (cacheDirectory.isTemporary()) {
+            tempFile.delete();
+        }
         super.finalize();
     }
 
@@ -104,9 +110,12 @@ Entry put(long key, long value) {
         long index = getIndex(key);
 
         while (true) {
-            if (getID(index) == 0L) {
+            long mapKey = getID(index);
+            if (mapKey == 0L) {
                 putID(index, key);
                 return createEntry(index,value);
+            } else if (mapKey == key) {
+                return createEntry(index);
             }
 
             index = getNextIndex(index);
@@ -118,7 +127,7 @@ private void setDumpBuffer(RandomAccessFile file) throws IOException {
 
         try {
             if (length > Integer.MAX_VALUE) {
-                dumpBuffer = new LongMemoryMappedData(file, length);
+                dumpBuffer = new LongMemoryMappedData(file, length, ENTRY_SIZE);
             } else {
                 dumpBuffer = new MemoryMappedData(file, length);
             }
@@ -161,6 +170,31 @@ void putFoffset(long index,long key) {
         }
     }
 
+    //---- Serialization support
+    void writeToStream(DataOutputStream out) throws IOException {
+        out.writeLong(keys);
+        out.writeInt(ID_SIZE);
+        out.writeInt(FOFFSET_SIZE);
+        out.writeInt(VALUE_SIZE);
+        out.writeUTF(tempFile.getAbsolutePath());
+        dumpBuffer.force(tempFile);
+    }
+
+    AbstractLongMap(DataInputStream dis, CacheDirectory cacheDir) throws IOException {
+        keys = dis.readLong();
+        ID_SIZE = dis.readInt();
+        FOFFSET_SIZE = dis.readInt();
+        VALUE_SIZE = dis.readInt();
+        tempFile = cacheDir.getCacheFile(dis.readUTF());
+        
+        KEY_SIZE = ID_SIZE;
+        ENTRY_SIZE = KEY_SIZE + VALUE_SIZE;
+        fileSize = keys * ENTRY_SIZE;
+        RandomAccessFile file = new RandomAccessFile(tempFile, "rw"); // NOI18N
+        setDumpBuffer(file);
+        cacheDirectory = cacheDir;
+    }
+    
     private long getIndex(long key) {
         long hash = key & 0x7FFFFFFFFFFFFFFFL;
         return (hash % keys) * ENTRY_SIZE;
@@ -198,6 +232,8 @@ private static boolean isLinux() {
         void putInt(long index, int data);
 
         void putLong(long index, long data);
+
+        void force(File bufferFile) throws IOException;
     }
 
     private class FileData implements Data {
@@ -279,12 +315,7 @@ private int loadBufferIfNeeded(long index) {
 
             if (offset != newOffset) {
                 try {
-                    if (bufferModified) {
-                        file.seek(offset);
-                        file.write(buf,0,getBufferSize(offset));
-                        bufferModified = false;
-                    }
-
+                    flush();
                     file.seek(newOffset);
                     file.readFully(buf,0,getBufferSize(newOffset));
                 } catch (IOException ex) {
@@ -306,6 +337,18 @@ private int getBufferSize(long off) {
             return size;
         }
 
+        private void flush() throws IOException {
+            if (bufferModified) {
+                file.seek(offset);
+                file.write(buf,0,getBufferSize(offset));
+                bufferModified = false;
+            }
+        }
+
+        @Override
+        public void force(File bufferFile) throws IOException {
+            flush();
+        }
     }
     
     private static class MemoryMappedData implements Data {
@@ -320,9 +363,7 @@ private int getBufferSize(long off) {
 
         MemoryMappedData(RandomAccessFile file, long length)
                   throws IOException {
-            FileChannel channel = file.getChannel();
-            buf = channel.map(MAP_MODE, 0, length);                
-            channel.close();
+            buf = createBuffer(file, length);
         }
 
         //~ Methods --------------------------------------------------------------------------------------------------------------
@@ -350,6 +391,30 @@ public void putInt(long index, int data) {
         public void putLong(long index, long data) {
             buf.putLong((int) index, data);
         }
+
+        @Override
+        public void force(File bufferFile) throws IOException {
+            if (MAP_MODE == FileChannel.MapMode.PRIVATE) {
+                File newBufferFile = new File(bufferFile.getAbsolutePath()+".new"); // NOI18N
+                int length = buf.capacity();
+                new FileOutputStream(newBufferFile).getChannel().write(buf);
+                buf = null;
+                bufferFile.delete();
+                newBufferFile.renameTo(bufferFile);
+                buf = createBuffer(new RandomAccessFile(bufferFile, "rw"), length); // NOI18N
+            } else {
+                buf.force();
+            }
+        }
+
+        private static MappedByteBuffer createBuffer(RandomAccessFile file, long length) throws IOException {
+            FileChannel channel = file.getChannel();
+            try {
+                return channel.map(MAP_MODE, 0, length);
+            } finally {
+                channel.close();
+            }
+        }
     }
 
     private static class LongMemoryMappedData implements Data {
@@ -362,22 +427,15 @@ public void putLong(long index, long data) {
         //~ Instance fields ----------------------------------------------------------------------------------------------------------
 
         private MappedByteBuffer[] dumpBuffer;
+        private final int entrySize;
 
 
         //~ Constructors ---------------------------------------------------------------------------------------------------------
 
-        LongMemoryMappedData(RandomAccessFile file, long length)
+        LongMemoryMappedData(RandomAccessFile file, long length, int entry)
                   throws IOException {
-            FileChannel channel = file.getChannel();
-            dumpBuffer = new MappedByteBuffer[(int) (((length + BUFFER_SIZE) - 1) / BUFFER_SIZE)];
-
-            for (int i = 0; i < dumpBuffer.length; i++) {
-                long position = i * BUFFER_SIZE;
-                long size = Math.min(BUFFER_SIZE + BUFFER_EXT, length - position);
-                dumpBuffer[i] = channel.map(MemoryMappedData.MAP_MODE, position, size);
-            }
-
-            channel.close();
+            dumpBuffer = createBuffers(file, length);
+            entrySize = entry;
         }
 
         //~ Methods --------------------------------------------------------------------------------------------------------------
@@ -413,6 +471,49 @@ private int getBufferIndex(long index) {
         private int getBufferOffset(long index) {
             return (int) (index & BUFFER_SIZE_MASK);
         }
-    }
 
+        @Override
+        public void force(File bufferFile) throws IOException{
+            if (MemoryMappedData.MAP_MODE == FileChannel.MapMode.PRIVATE) {
+                File newBufferFile = new File(bufferFile.getAbsolutePath()+".new"); // NOI18N
+                long length = bufferFile.length();
+                FileChannel channel = new FileOutputStream(newBufferFile).getChannel();
+                int offset_start = 0;
+
+                for (int i = 0; i < dumpBuffer.length; i++) {
+                    MappedByteBuffer buf = dumpBuffer[i];
+                    long offset_end = (((i+1)*BUFFER_SIZE)/entrySize)*entrySize + entrySize;
+
+                    if (offset_end > length) {
+                        offset_end = length;
+                    }
+                    buf.limit((int)(offset_end - i*BUFFER_SIZE));
+                    buf.position(offset_start);
+                    channel.write(buf);
+                    offset_start = (int)(offset_end - (i+1)*BUFFER_SIZE);
+                }
+                dumpBuffer = null;
+                bufferFile.delete();
+                newBufferFile.renameTo(bufferFile);
+                dumpBuffer = createBuffers(new RandomAccessFile(bufferFile, "rw"), length); // NOI18N
+            } else {
+                for (MappedByteBuffer buf : dumpBuffer) {
+                    buf.force();
+                }
+            }
+        }
+
+        private static MappedByteBuffer[] createBuffers(RandomAccessFile file, long length) throws IOException {
+            FileChannel channel = file.getChannel();
+            MappedByteBuffer[] dumpBuffer = new MappedByteBuffer[(int) (((length + BUFFER_SIZE) - 1) / BUFFER_SIZE)];
+
+            for (int i = 0; i < dumpBuffer.length; i++) {
+                long position = i * BUFFER_SIZE;
+                long size = Math.min(BUFFER_SIZE + BUFFER_EXT, length - position);
+                dumpBuffer[i] = channel.map(MemoryMappedData.MAP_MODE, position, size);
+            }
+            channel.close();
+            return dumpBuffer;
+        }
+    }
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/CacheDirectory.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/CacheDirectory.java
new file mode 100644
index 000000000..635f6089a
--- /dev/null
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/CacheDirectory.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.lib.profiler.heap;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ *
+ * @author Tomas Hurka
+ */
+class CacheDirectory {
+    
+    private static final String DIR_EXT = ".hwcache";   // NOI18N
+    private static final String DUMP_AUX_FILE = "NBProfiler.nphd";   // NOI18N
+    
+    private File cacheDirectory;
+    
+    static CacheDirectory getHeapDumpCacheDirectory(File heapDump) {
+        String dumpName = heapDump.getName();
+        File parent = heapDump.getParentFile();
+        File dir = new File(parent, dumpName+DIR_EXT);
+        return new CacheDirectory(dir);
+    }
+    
+    CacheDirectory(File cacheDir) {
+        cacheDirectory = cacheDir;
+        if (cacheDir != null) {
+            if (!cacheDir.exists()) {
+                if (!cacheDir.mkdir()) {
+                    cacheDirectory = null;
+                }
+            }
+        }
+        if (cacheDirectory != null) {
+            assert cacheDirectory.isDirectory() && cacheDirectory.canRead() && cacheDirectory.canWrite();            
+        }
+    }
+    
+    File createTempFile(String prefix, String suffix) throws IOException {
+        File newFile;
+        
+        if (isTemporary()) {
+            newFile = File.createTempFile(prefix, suffix);
+            newFile.deleteOnExit();
+        } else {
+            newFile = File.createTempFile(prefix, suffix, cacheDirectory);
+        }
+        return newFile;
+    }
+    
+    File getHeapDumpAuxFile() {
+        assert !isTemporary();
+        return new File(cacheDirectory, DUMP_AUX_FILE);
+    }
+    
+    boolean isTemporary() {
+        return cacheDirectory == null;
+    }
+
+    File getCacheFile(String fileName) throws FileNotFoundException {
+        File f = new File(fileName);
+        if (isFileRW(f)) {
+            return f;
+        }
+        // try to find file in cache directory
+        f = new File(cacheDirectory, f.getName());
+        if (isFileRW(f)) {
+            return f;
+        }
+        throw new FileNotFoundException(fileName);
+    }
+
+    File getHeapFile(String fileName) throws FileNotFoundException {
+        File f = new File(fileName);
+        if (isFileR(f)) {
+            return f;
+        }
+        // try to find heap dump file next to cache directory
+        f = new File(cacheDirectory.getParentFile(), f.getName());
+        if (isFileR(f)) {
+            return f;
+        }
+        throw new FileNotFoundException(fileName);        
+    }
+    
+    private static boolean isFileR(File f) {
+        return f.exists() && f.isFile() && f.canRead();
+    }
+    
+    private static boolean isFileRW(File f) {
+        return isFileR(f) && f.canWrite();
+    }
+
+    private static boolean isLinux() {
+        String osName = System.getProperty("os.name");  // NOI18N
+
+        return osName.endsWith("Linux"); // NOI18N
+    }
+}
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/ClassDump.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/ClassDump.java
index 6432deef4..447c228ec 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/ClassDump.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/ClassDump.java
@@ -19,6 +19,9 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -109,8 +112,11 @@ public int getInstanceSize() {
             return -1;
         }
 
-        return classDumpSegment.getMinimumInstanceSize()
-               + getHprofBuffer().getInt(fileOffset + classDumpSegment.instanceSizeOffset);
+        int size = getRawInstanceSize();
+        if (!classDumpSegment.newSize) {
+            size += classDumpSegment.getMinimumInstanceSize();
+        }
+        return size;
     }
 
     public long getRetainedSizeByClass() {
@@ -333,6 +339,10 @@ int getConstantPoolSize() {
         return (int) (cpOffset - (fileOffset + classDumpSegment.constantPoolSizeOffset));
     }
 
+    int getRawInstanceSize() {
+        return getHprofBuffer().getInt(fileOffset + classDumpSegment.instanceSizeOffset);
+    }
+
     HprofHeap getHprof() {
         return classDumpSegment.hprofHeap;
     }
@@ -502,4 +512,22 @@ public Object next() {
             throw new NoSuchElementException();
         } 
     }
+
+    //---- Serialization support
+    void writeToStream(DataOutputStream out) throws IOException {
+        out.writeLong(fileOffset);
+        out.writeInt(instances);
+        out.writeLong(firstInstanceOffset);
+        out.writeLong(loadClassOffset);
+        out.writeLong(retainedSizeByClass);        
+    }
+
+    ClassDump(ClassDumpSegment segment, long offset, DataInputStream dis) throws IOException {
+        this(segment, offset);
+        instances = dis.readInt();
+        firstInstanceOffset = dis.readLong();
+        loadClassOffset = dis.readLong();
+        retainedSizeByClass = dis.readLong();        
+    }
+
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/ClassDumpSegment.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/ClassDumpSegment.java
index 5009175dc..7055a2bb7 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/ClassDumpSegment.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/ClassDumpSegment.java
@@ -19,6 +19,9 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -55,6 +58,7 @@
     final int stackTraceSerialNumberOffset;
     final int superClassIDOffset;
     ClassDump java_lang_Class;
+    boolean newSize;
     Map /*<JavaClass,List<Field>>*/ fieldsCache;
     private List /*<JavaClass>*/ classes;
     private Map /*<Byte,JavaClass>*/ primitiveArrayMap;
@@ -229,7 +233,8 @@ void addInstanceSize(ClassDump cls, int tag, long instanceOffset) {
         return classes;
     }
 
-    private void extractSpecialClasses() {
+    void extractSpecialClasses() {
+        ClassDump java_lang_Object = null;
         primitiveArrayMap = new HashMap();
 
         Iterator classIt = classes.iterator();
@@ -257,6 +262,8 @@ private void extractSpecialClasses() {
                 typeObj = Integer.valueOf(HprofHeap.LONG);
             } else if (vmName.equals("java/lang/Class")) { // NOI18N
                 java_lang_Class = jcls;
+            } else if (vmName.equals("java/lang/Object")) { // NOI18N
+                java_lang_Object = jcls;
             } else if (vmName.equals("boolean[]")) { // NOI18N
                 typeObj = Integer.valueOf(HprofHeap.BOOLEAN);
             } else if (vmName.equals("char[]")) { // NOI18N
@@ -275,12 +282,56 @@ private void extractSpecialClasses() {
                 typeObj = Integer.valueOf(HprofHeap.LONG);
             } else if (vmName.equals("java.lang.Class")) { // NOI18N
                 java_lang_Class = jcls;
+            } else if (vmName.equals("java.lang.Object")) { // NOI18N
+                java_lang_Object = jcls;
             }
 
             if (typeObj != null) {
                 primitiveArrayMap.put(typeObj, jcls);
             }
         }
+        if (java_lang_Object != null) {
+            newSize = java_lang_Object.getRawInstanceSize() > 0;
+        }
+    }
+
+    //---- Serialization support
+    void writeToStream(DataOutputStream out) throws IOException {
+        super.writeToStream(out);
+        if (classes == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(classes.size());
+            for (int i=0; i<classes.size(); i++) {
+                ClassDump classDump = (ClassDump) classes.get(i);
+
+                classDump.writeToStream(out);
+                Long size = (Long) arrayMap.get(classDump);
+                out.writeBoolean(size != null);
+                if (size != null) {
+                    out.writeLong(size.longValue());
+                }
+            }
+        }
+    }
+
+    ClassDumpSegment(HprofHeap heap, long start, long end, DataInputStream dis) throws IOException {
+        this(heap, start, end);
+        int classesSize = dis.readInt();
+        if (classesSize != 0) {
+            List cls = new ArrayList /*<JavaClass>*/(classesSize);
+            arrayMap = new HashMap(classesSize / 15);
+            
+            for (int i=0; i<classesSize; i++) {
+                ClassDump c = new ClassDump(this, dis.readLong(), dis);
+                cls.add(c);
+                if (dis.readBoolean()) {
+                    Long size = Long.valueOf(dis.readLong());
+                    arrayMap.put(c, size);
+                }
+            }
+            classes = Collections.unmodifiableList(cls);
+        }
     }
     
     private static class FieldsCache extends LinkedHashMap {
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/DomMap.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/DomMap.java
index 2c44341da..61145e22b 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/DomMap.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/DomMap.java
@@ -71,8 +71,8 @@ long getIdom() {
     
     //~ Constructors -------------------------------------------------------------------------------------------------------------
 
-    DomMap(int size,int idSize,int foffsetSize) throws FileNotFoundException, IOException {
-        super(size,idSize,foffsetSize,idSize);
+    DomMap(int size,int idSize,int foffsetSize, CacheDirectory cacheDir) throws FileNotFoundException, IOException {
+        super(size,idSize,foffsetSize,idSize,cacheDir);
     }
 
     //~ Methods ------------------------------------------------------------------------------------------------------------------
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/DominatorTree.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/DominatorTree.java
index bc12765c5..09c2be13c 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/DominatorTree.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/DominatorTree.java
@@ -19,10 +19,11 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -117,14 +118,13 @@ private boolean computeOneLevel(boolean ignoreDirty) throws IOException {
             if (oldIdom == -1 || (oldIdom > 0 && (ignoreDirty || dirtySet.contains(oldIdom) || dirtySet.contains(instanceId)))) {            
 //processedId++;
                 LongMap.Entry entry = heap.idToOffsetMap.get(instanceId);
-                List refs = entry.getReferences();
-                Iterator refIt = refs.iterator();
-                long newIdomId = ((Long)refIt.next()).longValue();
+                LongIterator refIt = entry.getReferences();
+                long newIdomId = refIt.next();
                 boolean dirty = false;
                 
                 while(refIt.hasNext() && newIdomId != 0) {
-                    Long refIdObj = (Long)refIt.next();
-                    newIdomId = intersect(newIdomId, refIdObj.longValue());
+                    long refIdObj = refIt.next();
+                    newIdomId = intersect(newIdomId, refIdObj);
                 }
                 if (oldIdom == -1) {
 //addedBynewDirtySet.add(newDirtySet.contains(instanceId) && !dirtySet.contains(instanceId));
@@ -345,6 +345,16 @@ String printInstance(Long instanceid) {
         return ii.getJavaClass().getName()+"#"+ii.getInstanceNumber();
         
     }
+
+    //---- Serialization support
+    void writeToStream(DataOutputStream out) throws IOException {
+        map.writeToStream(out);
+    }
+
+    DominatorTree(HprofHeap h, DataInputStream dis) throws IOException {
+        heap = h;
+        map = new LongHashMap(dis);
+    }
     
     private static final class NearestGCRootCache extends LinkedHashMap {
         private final int maxSize;
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/GCRoot.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/GCRoot.java
index d7731cbc7..1b42841e1 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/GCRoot.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/GCRoot.java
@@ -73,6 +73,36 @@
      */
     public static final String UNKNOWN = "unknown"; // NOI18N
 
+    /**
+     * Interned string GC root kind.
+     */
+    public static final String INTERNED_STRING = "interned string"; // NOI18N
+
+    /**
+     * Finalizing GC root kind.
+     */
+    public static final String FINALIZING = "finalizing"; // NOI18N
+
+    /**
+     * Debugger GC root kind.
+     */
+    public static final String DEBUGGER = "debugger"; // NOI18N
+
+    /**
+     * Reference cleanup GC root kind.
+     */
+    public static final String REFERENCE_CLEANUP = "reference cleanup"; // NOI18N
+
+    /**
+     * VM internal GC root kind.
+     */
+    public static final String VM_INTERNAL = "VM internal"; // NOI18N
+
+    /**
+     * JNI monitor GC root kind.
+     */
+    public static final String JNI_MONITOR = "JNI monitor"; // NOI18N
+
     //~ Methods ------------------------------------------------------------------------------------------------------------------
 
     /**
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/Heap.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/Heap.java
index 623d83119..0a37fbf63 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/Heap.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/Heap.java
@@ -140,4 +140,7 @@
      * this {@link Heap}
      */
     Properties getSystemProperties();
+
+    boolean isRetainedSizeComputed();
+    boolean isRetainedSizeByClassComputed();
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/HeapFactory.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/HeapFactory.java
index 42cc5647e..b9a0a097d 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/HeapFactory.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/HeapFactory.java
@@ -19,9 +19,13 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 
 
 /**
@@ -60,6 +64,31 @@ public static Heap createHeap(File heapDump) throws FileNotFoundException, IOExc
      */
     public static Heap createHeap(File heapDump, int segment)
                            throws FileNotFoundException, IOException {
-        return new HprofHeap(heapDump, segment);
+        CacheDirectory cacheDir = CacheDirectory.getHeapDumpCacheDirectory(heapDump);
+        if (!cacheDir.isTemporary()) {
+            File savedDump = cacheDir.getHeapDumpAuxFile();
+
+            if (savedDump.exists() && savedDump.isFile() && savedDump.canRead()) {
+                try {
+                    return loadHeap(cacheDir);
+                } catch (IOException ex) {
+                    System.err.println("Loading heap dump "+heapDump+" from cache failed.");
+                    ex.printStackTrace(System.err);
+                }
+            }
+        }
+        return new HprofHeap(heapDump, segment, cacheDir);
+
+    }
+    
+    static Heap loadHeap(CacheDirectory cacheDir)
+                           throws FileNotFoundException, IOException {
+        File savedDump = cacheDir.getHeapDumpAuxFile();
+        InputStream is = new BufferedInputStream(new FileInputStream(savedDump), 64*1024);
+        DataInputStream dis = new DataInputStream(is);
+        Heap heap = new HprofHeap(dis, cacheDir);
+        dis.close();
+        return heap;
     }
+    
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/HeapProgress.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/HeapProgress.java
index 984223ff9..3b44ad27f 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/HeapProgress.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/HeapProgress.java
@@ -31,19 +31,19 @@
     
     public static final int PROGRESS_MAX = 1000;
     private static ThreadLocal progressThreadLocal = new ThreadLocal();
-    
+
     private HeapProgress() {
         
     }
     
     public static BoundedRangeModel getProgress() {
-        BoundedRangeModel model = (BoundedRangeModel) progressThreadLocal.get();
+        ModelInfo info = (ModelInfo) progressThreadLocal.get();
         
-        if (model == null) {
-            model = new DefaultBoundedRangeModel(0,0,0,PROGRESS_MAX);
-            progressThreadLocal.set(model);
+        if (info == null) {
+            info = new ModelInfo();
+            progressThreadLocal.set(info);
         }
-        return model;
+        return info.model;
     }
     
     static void progress(long counter, long startOffset, long value, long endOffset) {
@@ -58,18 +58,39 @@ static void progress(long value, long endValue) {
     }
     
     private static void progress(final long value, final long endOffset, final long startOffset) {
-        BoundedRangeModel model = (BoundedRangeModel) progressThreadLocal.get();
-        if (model != null) {
+        ModelInfo info = (ModelInfo) progressThreadLocal.get();
+        if (info != null) {
+            if (info.level>info.divider) {
+                info.divider = info.level;
+            }
             long val = PROGRESS_MAX*(value - startOffset)/(endOffset - startOffset);
-            setValue(model, (int)val);
+            int modelVal = (int) (info.offset + val/info.divider);
+            setValue(info.model, modelVal);
         }
     }
     
+    private static int levelAdd(ModelInfo info, int diff) {        
+        info.level+=diff;
+        return info.level;
+    }
+
+    static void progressStart() {
+        ModelInfo info = (ModelInfo) progressThreadLocal.get();
+        if (info != null) {
+            levelAdd(info, 1);
+        }
+    }
+
     static void progressFinish() {
-        BoundedRangeModel model = (BoundedRangeModel) progressThreadLocal.get();
-        if (model != null) {
-            setValue(model, PROGRESS_MAX);
-            progressThreadLocal.remove();
+        ModelInfo info = (ModelInfo) progressThreadLocal.get();
+        if (info != null) {
+            int level = levelAdd(info, -1);
+
+            assert level >= 0;
+            if (level == 0) {
+                progressThreadLocal.remove();
+            }
+            info.offset = info.model.getValue();
         }
     }
     
@@ -82,4 +103,15 @@ private static void setValue(final BoundedRangeModel model, final int val) {
             });
         }
     }
+    
+    private static class ModelInfo {
+        private BoundedRangeModel model;
+        private int level;
+        private int divider;
+        private int offset;
+
+        private ModelInfo() {
+            model = new DefaultBoundedRangeModel(0,0,0,PROGRESS_MAX);
+        } 
+    }
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofByteBuffer.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofByteBuffer.java
index 338440a77..da959d775 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofByteBuffer.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofByteBuffer.java
@@ -35,8 +35,10 @@
     // Magic header
     static final String magic1 = "JAVA PROFILE 1.0.1"; // NOI18N
     static final String magic2 = "JAVA PROFILE 1.0.2"; // NOI18N
+    static final String magic3 = "JAVA PROFILE 1.0.3"; // NOI18N
     static final int JAVA_PROFILE_1_0_1 = 1;
     static final int JAVA_PROFILE_1_0_2 = 2;
+    static final int JAVA_PROFILE_1_0_3 = 3;
     static final int MINIMAL_SIZE = 30;
     static final boolean DEBUG = false;
 
@@ -135,6 +137,8 @@ void readHeader() throws IOException {
             version = JAVA_PROFILE_1_0_1;
         } else if (magic2.equals(magic)) {
             version = JAVA_PROFILE_1_0_2;
+        } else if (magic3.equals(magic)) {
+            version = JAVA_PROFILE_1_0_3;
         } else {
             if (DEBUG) {
                 System.out.println("Invalid version"); // NOI18N
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoot.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoot.java
index 5ffc4613a..ee5d4f9c5 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoot.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoot.java
@@ -43,6 +43,13 @@
         kindMap.put(Integer.valueOf(HprofHeap.ROOT_THREAD_BLOCK), GCRoot.THREAD_BLOCK);
         kindMap.put(Integer.valueOf(HprofHeap.ROOT_MONITOR_USED), GCRoot.MONITOR_USED);
         kindMap.put(Integer.valueOf(HprofHeap.ROOT_THREAD_OBJECT), GCRoot.THREAD_OBJECT);
+        // HPROF HEAP 1.0.3
+        kindMap.put(Integer.valueOf(HprofHeap.ROOT_INTERNED_STRING), GCRoot.INTERNED_STRING);
+        kindMap.put(Integer.valueOf(HprofHeap.ROOT_FINALIZING), GCRoot.FINALIZING);
+        kindMap.put(Integer.valueOf(HprofHeap.ROOT_DEBUGGER), GCRoot.DEBUGGER);
+        kindMap.put(Integer.valueOf(HprofHeap.ROOT_REFERENCE_CLEANUP), GCRoot.REFERENCE_CLEANUP);
+        kindMap.put(Integer.valueOf(HprofHeap.ROOT_VM_INTERNAL), GCRoot.VM_INTERNAL);
+        kindMap.put(Integer.valueOf(HprofHeap.ROOT_JNI_MONITOR), GCRoot.JNI_MONITOR);
     }
 
     //~ Instance fields ----------------------------------------------------------------------------------------------------------
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoots.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoots.java
index a8dbc2a3e..8fa55cd2d 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoots.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofGCRoots.java
@@ -57,6 +57,14 @@ Collection getGCRoots() {
                 gcRoots.putAll(computeGCRootsFor(heap.getHeapTagBound(HprofHeap.ROOT_MONITOR_USED)));
                 gcRoots.putAll(computeGCRootsFor(heap.getHeapTagBound(HprofHeap.ROOT_THREAD_OBJECT)));
 
+                // HPROF HEAP 1.0.3
+                gcRoots.putAll(computeGCRootsFor(heap.getHeapTagBound(HprofHeap.ROOT_INTERNED_STRING)));
+                gcRoots.putAll(computeGCRootsFor(heap.getHeapTagBound(HprofHeap.ROOT_FINALIZING)));
+                gcRoots.putAll(computeGCRootsFor(heap.getHeapTagBound(HprofHeap.ROOT_DEBUGGER)));
+                gcRoots.putAll(computeGCRootsFor(heap.getHeapTagBound(HprofHeap.ROOT_REFERENCE_CLEANUP)));
+                gcRoots.putAll(computeGCRootsFor(heap.getHeapTagBound(HprofHeap.ROOT_VM_INTERNAL)));
+                gcRoots.putAll(computeGCRootsFor(heap.getHeapTagBound(HprofHeap.ROOT_JNI_MONITOR)));
+
                 List rootList = new ArrayList(gcRoots.values());
                 Collections.sort(rootList, new Comparator() {
                     public int compare(Object o1, Object o2) {
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofHeap.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofHeap.java
index 38a1585a2..ed118b049 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofHeap.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofHeap.java
@@ -19,8 +19,12 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -69,6 +73,17 @@
     static final int OBJECT_ARRAY_DUMP = 0x22;
     static final int PRIMITIVE_ARRAY_DUMP = 0x23;
 
+    //  HPROF HEAP 1.0.3 tags
+    static final int HEAP_DUMP_INFO                = 0xfe;
+    static final int ROOT_INTERNED_STRING          = 0x89;
+    static final int ROOT_FINALIZING               = 0x8a;
+    static final int ROOT_DEBUGGER                 = 0x8b;
+    static final int ROOT_REFERENCE_CLEANUP        = 0x8c;
+    static final int ROOT_VM_INTERNAL              = 0x8d;
+    static final int ROOT_JNI_MONITOR              = 0x8e;
+    static final int UNREACHABLE                   = 0x90; /* deprecated */
+    static final int PRIMITIVE_ARRAY_NODATA_DUMP   = 0xc3;
+
     // basic type
     static final int OBJECT = 2;
     static final int BOOLEAN = 4;
@@ -81,6 +96,9 @@
     static final int LONG = 11;
     private static final boolean DEBUG = false;
 
+    private static final String SNAPSHOT_ID = "NBPHD";
+    private static final int SNAPSHOT_VERSION  = 2;
+    
     //~ Instance fields ----------------------------------------------------------------------------------------------------------
 
     HprofByteBuffer dumpBuffer;
@@ -88,21 +106,31 @@
     private NearestGCRoot nearestGCRoot;
     final HprofGCRoots gcRoots;
     private ComputedSummary computedSummary;
+    private final Object computedSummaryLock = new Object();
     private DominatorTree domTree;
     private TagBounds allInstanceDumpBounds;
     private TagBounds heapDumpSegment;
     private TagBounds[] heapTagBounds;
     private TagBounds[] tagBounds = new TagBounds[0xff];
     private boolean instancesCountComputed;
+    private final Object instancesCountLock = new Object();
     private boolean referencesComputed;
+    private final Object referencesLock = new Object();
     private boolean retainedSizeComputed;
+    private final Object retainedSizeLock = new Object();
     private boolean retainedSizeByClassComputed;
+    private final Object retainedSizeByClassLock = new Object();
     private int idMapSize;
     private int segment;
 
+    // for serialization
+    File heapDumpFile;
+    CacheDirectory cacheDirectory;
+    
     //~ Constructors -------------------------------------------------------------------------------------------------------------
 
-    HprofHeap(File dumpFile, int seg) throws FileNotFoundException, IOException {
+    HprofHeap(File dumpFile, int seg, CacheDirectory cacheDir) throws FileNotFoundException, IOException {
+        cacheDirectory = cacheDir;
         dumpBuffer = HprofByteBuffer.createHprofByteBuffer(dumpFile);
         segment = seg;
         fillTagBounds(dumpBuffer.getHeaderSize());
@@ -112,9 +140,10 @@
             fillHeapTagBounds();
         }
 
-        idToOffsetMap = new LongMap(idMapSize,dumpBuffer.getIDSize(),dumpBuffer.getFoffsetSize());
+        idToOffsetMap = new LongMap(idMapSize,dumpBuffer.getIDSize(),dumpBuffer.getFoffsetSize(), cacheDirectory);
         nearestGCRoot = new NearestGCRoot(this);
         gcRoots = new HprofGCRoots(this);
+        heapDumpFile = dumpFile;
     }
 
     //~ Methods ------------------------------------------------------------------------------------------------------------------
@@ -234,8 +263,10 @@ public synchronized HeapSummary getSummary() {
             return new Summary(dumpBuffer, summaryBound.startOffset);
         }
 
-        if (computedSummary == null) {
-            computedSummary = new ComputedSummary(this);
+        synchronized (computedSummaryLock) {
+            if (computedSummary == null) {
+                computedSummary = new ComputedSummary(this);
+            }
         }
 
         return computedSummary;
@@ -256,6 +287,83 @@ public Properties getSystemProperties() {
         return null;
     }
 
+    public boolean isRetainedSizeComputed() {
+        return retainedSizeComputed;
+    }
+
+    public boolean isRetainedSizeByClassComputed() {
+        return retainedSizeByClassComputed;
+    }
+
+    //---- Serialization support
+    void writeToFile() {
+        if (!cacheDirectory.isTemporary()) {
+            try {
+                DataOutputStream out;
+                File outFile = cacheDirectory.getHeapDumpAuxFile();
+                out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile), 32768));
+                writeToStream(out);
+                out.close();
+            } catch (IOException ex) {
+                ex.printStackTrace(System.err);
+            }
+        }
+    }
+    
+    void writeToStream(DataOutputStream out) throws IOException {
+        out.writeUTF(SNAPSHOT_ID);
+        out.writeInt(SNAPSHOT_VERSION);
+        out.writeUTF(heapDumpFile.getAbsolutePath());
+        nearestGCRoot.writeToStream(out);
+        allInstanceDumpBounds.writeToStream(out);
+        heapDumpSegment.writeToStream(out);
+        TagBounds.writeToStream(heapTagBounds, out);
+        TagBounds.writeToStream(tagBounds, out);
+        out.writeBoolean(instancesCountComputed);
+        out.writeBoolean(referencesComputed);
+        out.writeBoolean(retainedSizeComputed);
+        out.writeBoolean(retainedSizeByClassComputed);
+        out.writeInt(idMapSize);
+        out.writeInt(segment);        
+        idToOffsetMap.writeToStream(out);
+        out.writeBoolean(domTree != null);
+        if (domTree != null) {
+            domTree.writeToStream(out);
+        }
+    }
+
+    HprofHeap(DataInputStream dis, CacheDirectory cacheDir) throws IOException {
+        String id = dis.readUTF();
+        if (!SNAPSHOT_ID.equals(id)) {
+            throw new IOException("Invalid HPROF dump id "+id);
+        }
+        int version = dis.readInt();
+        if (version != SNAPSHOT_VERSION) {
+            throw new IOException("Invalid HPROF version "+SNAPSHOT_VERSION+" loaded "+version);            
+        }
+        heapDumpFile = cacheDir.getHeapFile(dis.readUTF());
+        cacheDirectory = cacheDir;
+        dumpBuffer = HprofByteBuffer.createHprofByteBuffer(heapDumpFile);
+        nearestGCRoot = new NearestGCRoot(this, dis);
+        allInstanceDumpBounds = new TagBounds(dis);
+        heapDumpSegment = new TagBounds(dis);
+        heapTagBounds = new TagBounds[0x100];
+        TagBounds.readFromStream(dis, this, heapTagBounds);
+        TagBounds.readFromStream(dis, this, tagBounds);        
+        instancesCountComputed = dis.readBoolean();
+        referencesComputed = dis.readBoolean();
+        retainedSizeComputed = dis.readBoolean();
+        retainedSizeByClassComputed = dis.readBoolean();
+        idMapSize = dis.readInt();
+        segment = dis.readInt();
+        idToOffsetMap = new LongMap(dis, cacheDirectory);
+        if (dis.readBoolean()) {
+            domTree = new DominatorTree(this, dis);
+        }
+        gcRoots = new HprofGCRoots(this);
+        getClassDumpSegment().extractSpecialClasses();            
+    }
+    
     ClassDumpSegment getClassDumpSegment() {
         return (ClassDumpSegment) heapTagBounds[CLASS_DUMP];
     }
@@ -365,11 +473,13 @@ Instance getInstanceByOffset(long[] offset, ClassDump instanceClassDump, long in
         }
     }
 
-    synchronized void computeInstances() {
+    void computeInstances() {
+        synchronized (instancesCountLock) {
         if (instancesCountComputed) {
             return;
         }
 
+        HeapProgress.progressStart();
         ClassDumpSegment classDumpBounds = getClassDumpSegment();
         int idSize = dumpBuffer.getIDSize();
         long[] offset = new long[] { allInstanceDumpBounds.startOffset };
@@ -412,8 +522,10 @@ synchronized void computeInstances() {
             }
             HeapProgress.progress(counter,allInstanceDumpBounds.startOffset,start,allInstanceDumpBounds.endOffset);
         }
-        HeapProgress.progressFinish();
         instancesCountComputed = true;
+        writeToFile();
+        }
+        HeapProgress.progressFinish();
     }
 
     List findReferencesFor(long instanceId) {
@@ -421,14 +533,13 @@ List findReferencesFor(long instanceId) {
         computeReferences();
         
         List refs = new ArrayList();
-        List refIds = idToOffsetMap.get(instanceId).getReferences();
-        Iterator refIdsIt = refIds.iterator();
+        LongIterator refIdsIt = idToOffsetMap.get(instanceId).getReferences();
         int idSize = dumpBuffer.getIDSize();
         ClassDumpSegment classDumpBounds = getClassDumpSegment();
         long[] offset = new long[1];
         
         while (refIdsIt.hasNext()) {
-            long foundInstanceId = ((Long)refIdsIt.next()).longValue();
+            long foundInstanceId = refIdsIt.next();
             offset[0] = idToOffsetMap.get(foundInstanceId).getOffset();
             long start = offset[0];
             int tag = readDumpTag(offset);
@@ -461,14 +572,12 @@ List findReferencesFor(long instanceId) {
                 }
             } else if (tag == OBJECT_ARRAY_DUMP) {
                 int elements = dumpBuffer.getInt(start + 1 + idSize + 4);
-                int i;
+                long classId = dumpBuffer.getID(start + 1 + idSize + 4 + 4);
+                ClassDump classDump = classDumpBounds.getClassDumpByID(classId);
                 long position = start + 1 + idSize + 4 + 4 + idSize;
 
-                for (i = 0; i < elements; i++, position += idSize) {
+                for (int i = 0; i < elements; i++, position += idSize) {
                     if (dumpBuffer.getID(position) == instanceId) {
-                        long classId = dumpBuffer.getID(start + 1 + idSize + 4 + 4);
-                        ClassDump classDump = classDumpBounds.getClassDumpByID(classId);
-
                         refs.add(new HprofArrayValue(classDump, start, i));
                     }
                 }
@@ -481,11 +590,13 @@ List findReferencesFor(long instanceId) {
         return refs;
     }
 
-    synchronized void computeReferences() {
+    void computeReferences() {
+        synchronized (referencesLock) {
         if (referencesComputed) {
             return;
         }
 
+        HeapProgress.progressStart();
         ClassDumpSegment classDumpBounds = getClassDumpSegment();
         int idSize = dumpBuffer.getIDSize();
         long[] offset = new long[] { allInstanceDumpBounds.startOffset };
@@ -564,11 +675,14 @@ synchronized void computeReferences() {
             }
         }
         idToOffsetMap.flush();
-        HeapProgress.progressFinish();        
         referencesComputed = true;
+        writeToFile();
+        }
+        HeapProgress.progressFinish();        
     }
     
-    synchronized void computeRetainedSize() {
+    void computeRetainedSize() {
+        synchronized (retainedSizeLock) {
         if (retainedSizeComputed) {
             return;
         }
@@ -626,9 +740,12 @@ synchronized void computeRetainedSize() {
             }
         }
         retainedSizeComputed = true;
+        writeToFile();
+        }
     }
 
-    synchronized void computeRetainedSizeByClass() {
+    void computeRetainedSizeByClass() {
+        synchronized (retainedSizeByClassLock) {
         if (retainedSizeByClassComputed) {
             return;
         }
@@ -661,15 +778,17 @@ synchronized void computeRetainedSizeByClass() {
         // all done, release domTree
         domTree = null;
         retainedSizeByClassComputed = true;
+        writeToFile();
+        }
     }
 
-    synchronized Instance getNearestGCRootPointer(Instance instance) {
+    Instance getNearestGCRootPointer(Instance instance) {
         return nearestGCRoot.getNearestGCRootPointer(instance);
     }
     
     int readDumpTag(long[] offset) {
         long position = offset[0];
-        int dumpTag = dumpBuffer.get(position++);
+        int dumpTag = dumpBuffer.get(position++) & 0xFF;
         long size = 0;
         long tagOffset = position;
         int idSize = dumpBuffer.getIDSize();
@@ -922,6 +1041,100 @@ int readDumpTag(long[] offset) {
 
                 break;
             }
+
+             /* HPROF HEAP 1.0.3 tags */
+            case HEAP_DUMP_INFO: {
+
+                if (DEBUG) {
+                    System.out.println("Tag HPROF_HEAP_DUMP_INFO"); // NOI18N
+                    int heapId = dumpBuffer.getInt(position);
+                    position += 4;
+
+                    long stringID = dumpBuffer.getID(position);
+                    position += idSize;
+                    System.out.println(" Dump info id " + heapId + " String ID " + stringID); // NOI18N
+                }
+
+                size = 4 + idSize;
+
+                break;
+            }
+            case ROOT_INTERNED_STRING: {
+
+                if (DEBUG) {
+                    System.out.println("Tag HPROF_ROOT_INTERNED_STRING"); // NOI18N
+                }
+
+                size = idSize;
+
+                break;
+          }
+            case ROOT_FINALIZING: {
+
+                if (DEBUG) {
+                    System.out.println("Tag HPROF_ROOT_FINALIZING"); // NOI18N
+                }
+
+                size = idSize;
+
+                break;
+            }
+            case ROOT_DEBUGGER: {
+
+                if (DEBUG) {
+                    System.out.println("Tag HPROF_ROOT_DEBUGGER"); // NOI18N
+                }
+
+                size = idSize;
+
+                break;
+            }
+            case ROOT_REFERENCE_CLEANUP: {
+
+                if (DEBUG) {
+                    System.out.println("Tag HPROF_ROOT_REFERENCE_CLEANUP"); // NOI18N
+                }
+
+                size = idSize;
+
+                break;
+            }
+            case ROOT_VM_INTERNAL: {
+
+                if (DEBUG) {
+                    System.out.println("Tag HPROF_ROOT_VM_INTERNAL"); // NOI18N
+                }
+
+                size = idSize;
+
+                break;
+            }
+            case ROOT_JNI_MONITOR: {
+
+                if (DEBUG) {
+                    System.out.println("Tag HPROF_ROOT_JNI_MONITOR"); // NOI18N
+                }
+
+                size = idSize;
+
+                break;
+            }
+            case UNREACHABLE: {
+
+                if (DEBUG) {
+                    System.out.println("Tag HPROF_UNREACHABLE"); // NOI18N
+                }
+
+                size = idSize;
+
+                break;
+            }
+            case PRIMITIVE_ARRAY_NODATA_DUMP: {
+                    throw new IllegalArgumentException(
+                        "Don't know how to load a nodata array");
+                //break;
+            }
+
             default:throw new IllegalArgumentException("Invalid dump tag " + dumpTag + " at position " + (position - 1)); // NOI18N              
         }
 
@@ -936,7 +1149,8 @@ int readTag(long[] offset) {
 
         //int time = dumpBuffer.getInt(start+1);
         long len = dumpBuffer.getInt(start + 1 + 4) & 0xFFFFFFFFL;  // len is unsigned int
-        if (len == 0 && tag != HEAP_DUMP_END) { // only HEAP_DUMP_END can have zero length
+         // only HEAP_DUMP_END can have zero length
+        if (len == 0 && tag != HEAP_DUMP_END && dumpBuffer.version != HprofByteBuffer.JAVA_PROFILE_1_0_3) {
             // broken tag length
             offset[0] = -1;
         } else {
@@ -991,6 +1205,7 @@ private void fillHeapTagBounds() {
             return;
         }
 
+        HeapProgress.progressStart();
         heapTagBounds = new TagBounds[0x100];
 
         long[] offset = new long[] { heapDumpSegment.startOffset + 1 + 4 + 4 };
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofInstanceObjectValue.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofInstanceObjectValue.java
index 3d651470b..897ee7eae 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofInstanceObjectValue.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofInstanceObjectValue.java
@@ -34,11 +34,11 @@
     //~ Methods ------------------------------------------------------------------------------------------------------------------
 
     public Instance getInstance() {
-        return instance.dumpClass.getHprof().getInstanceByID(getInstanceId());
+        return field.classDump.getHprof().getInstanceByID(getInstanceId());
     }
 
     long getInstanceId() {
-        HprofByteBuffer dumpBuffer = instance.dumpClass.getHprofBuffer();
+        HprofByteBuffer dumpBuffer = field.classDump.getHprofBuffer();
 
         return dumpBuffer.getID(fileOffset);
     }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofInstanceValue.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofInstanceValue.java
index b64aaae0e..9bf4d2b4f 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofInstanceValue.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofInstanceValue.java
@@ -28,20 +28,20 @@
     //~ Instance fields ----------------------------------------------------------------------------------------------------------
 
     HprofField field;
-    InstanceDump instance;
+    long instanceOffset;
 
     //~ Constructors -------------------------------------------------------------------------------------------------------------
 
-    HprofInstanceValue(InstanceDump i, HprofField f, long offset) {
-        super(offset);
-        instance = i;
+    HprofInstanceValue(InstanceDump i, HprofField f, long fieldOffset) {
+        super(fieldOffset);
+        instanceOffset = i.fileOffset;
         field = f;
     }
 
     //~ Methods ------------------------------------------------------------------------------------------------------------------
 
     public Instance getDefiningInstance() {
-        return instance;
+        return field.classDump.getHprof().getInstanceByOffset(new long[] {instanceOffset});
     }
 
     public Field getField() {
@@ -54,7 +54,7 @@ public String getValue() {
 
     Object getTypeValue() {
         byte type = field.getValueType();
-        HprofByteBuffer dumpBuffer = instance.dumpClass.getHprofBuffer();
+        HprofByteBuffer dumpBuffer = field.classDump.getHprofBuffer();
 
         return getTypeValue(dumpBuffer, fileOffset, type);
     }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofProxy.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofProxy.java
index e232a0302..5ef363287 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofProxy.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/HprofProxy.java
@@ -52,6 +52,11 @@ static Properties getProperties(Instance propertiesInstance) {
             if (map != null) {
                 entriesObj = (ObjectArrayDump) map.getValueOfField("table"); // NOI18N
                 return getPropertiesFromTable(entriesObj, props, "key", "val"); // NOI18N
+            } else {    // old Hashtable
+                entriesObj = (ObjectArrayDump) propertiesInstance.getValueOfField("elementData"); // NOI18N
+                if (entriesObj != null) {
+                    return getPropertiesFromTable(entriesObj, props, "key", "value");   // NOI18N
+                }
             }
         }
         return null;
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/LongBuffer.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/LongBuffer.java
index 57d259edd..28e217c5a 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/LongBuffer.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/LongBuffer.java
@@ -26,7 +26,6 @@
 import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -49,11 +48,13 @@
     private int bufferSize;
     private int readOffset;
     private int longs;
+    private CacheDirectory cacheDirectory;
 
     //~ Constructors -------------------------------------------------------------------------------------------------------------
 
-    LongBuffer(int size) {
+    LongBuffer(int size, CacheDirectory cacheDir) {
         buffer = new long[size];
+        cacheDirectory = cacheDir;
     }
 
     //~ Methods ------------------------------------------------------------------------------------------------------------------
@@ -144,8 +145,7 @@ void writeLong(long data) throws IOException {
         }
 
         if (backingFile == null) {
-            backingFile = File.createTempFile("NBProfiler", ".gc"); // NOI18N
-            backingFile.deleteOnExit();
+            backingFile = cacheDirectory.createTempFile("NBProfiler", ".gc"); // NOI18N
         }
 
         if (writeStream == null) {
@@ -162,7 +162,7 @@ void writeLong(long data) throws IOException {
     }
     
     LongBuffer revertBuffer() throws IOException {
-        LongBuffer reverted = new LongBuffer(buffer.length);
+        LongBuffer reverted = new LongBuffer(buffer.length, cacheDirectory);
         
         if (bufferSize < buffer.length) {
             for (int i=0;i<bufferSize;i++) {
@@ -186,4 +186,35 @@ int getSize() {
         return longs;
     }
     
+    // serialization support
+    void writeToStream(DataOutputStream out) throws IOException {
+        out.writeInt(bufferSize);
+        out.writeInt(readOffset);
+        out.writeInt(longs);
+        out.writeInt(buffer.length);
+        out.writeBoolean(useBackingFile);
+        if (useBackingFile) {
+            out.writeUTF(backingFile.getAbsolutePath());
+        } else {
+            for (int i=0; i<bufferSize; i++) {
+                out.writeLong(buffer[i]);
+            }
+        }
+    }
+
+    LongBuffer(DataInputStream dis, CacheDirectory cacheDir) throws IOException {
+        bufferSize = dis.readInt();
+        readOffset = dis.readInt();
+        longs = dis.readInt();
+        buffer = new long[dis.readInt()];
+        useBackingFile = dis.readBoolean();
+        if (useBackingFile) {
+            backingFile = cacheDir.getCacheFile(dis.readUTF());
+        } else {
+            for (int i=0; i<bufferSize; i++) {
+                buffer[i] = dis.readLong();
+            }
+        }
+        cacheDirectory = cacheDir;
+    } 
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/LongHashMap.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/LongHashMap.java
index f3be07d51..a3e433050 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/LongHashMap.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/LongHashMap.java
@@ -18,6 +18,9 @@
  */
 package org.netbeans.lib.profiler.heap;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Map;
 
@@ -553,5 +556,24 @@ public int hashCode() {
         return result;
     }
 
+    //---- Serialization support
+    void writeToStream(DataOutputStream out) throws IOException {
+        out.writeInt(modCount);
+        out.writeInt(size);
+        out.writeInt(threshold);
+        out.writeInt(table.length);
+        for (int i = 0; i < table.length; i++) {
+            out.writeLong(table[i]);
+        }
+    }
 
+    LongHashMap(DataInputStream dis) throws IOException {
+        modCount = dis.readInt();
+        size = dis.readInt();
+        threshold = dis.readInt();
+        table = new long[dis.readInt()];
+        for (int i = 0; i < table.length; i++) {
+            table[i] = dis.readLong();
+        }
+    }
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/LongIterator.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/LongIterator.java
new file mode 100644
index 000000000..1d70dbecf
--- /dev/null
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/LongIterator.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.lib.profiler.heap;
+
+import java.util.NoSuchElementException;
+
+/**
+ *
+ * @author Tomas Hurka
+ */
+abstract class LongIterator {
+
+    static LongIterator EMPTY_ITERATOR = new Empty();
+
+    static LongIterator singleton(long i) {
+        return new Singleton(i);
+    }
+
+    abstract boolean hasNext();
+
+    abstract long next();
+
+    private static class Empty extends LongIterator {
+
+        @Override
+        boolean hasNext() {
+            return false;
+        }
+
+        @Override
+        long next() {
+            throw new NoSuchElementException();
+        }
+    }
+
+    private static class Singleton extends LongIterator {
+
+        private final long item;
+        private boolean skipped;
+
+        private Singleton(long i) {
+            item = i;
+        }
+
+        @Override
+        boolean hasNext() {
+            return !skipped;
+        }
+
+        @Override
+        long next() {
+            if (hasNext()) {
+                skipped = true;
+                return item;
+            }
+            throw new NoSuchElementException();
+        }
+    }
+}
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/LongMap.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/LongMap.java
index b5bfa9787..034ae4f14 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/LongMap.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/LongMap.java
@@ -19,11 +19,11 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.util.Collections;
 import java.util.Iterator;
-import java.util.List;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
@@ -141,23 +141,23 @@ void addReference(long instanceId) {
             }
         }
         
-        List getReferences() {
+        LongIterator getReferences() {
             byte flags = getFlags();
             long ref = getReferencesPointer();
             if ((flags & NUMBER_LIST) == 0) {
                 if (ref == 0L) {
-                    return Collections.EMPTY_LIST;
+                    return LongIterator.EMPTY_ITERATOR;
                 } else {
-                    return Collections.singletonList(new Long(ref));
+                    return LongIterator.singleton(ref);
                 }
             } else {
                 try {
-                    return referenceList.getNumbers(ref);
+                    return referenceList.getNumbersIterator(ref);
                 } catch (IOException ex) {
                     ex.printStackTrace();
                 }
             }
-            return Collections.EMPTY_LIST;
+            return LongIterator.EMPTY_ITERATOR;
         }
         
         long getOffset() {
@@ -238,9 +238,9 @@ public int hashCode() {
     
     //~ Constructors -------------------------------------------------------------------------------------------------------------
 
-    LongMap(int size,int idSize,int foffsetSize) throws FileNotFoundException, IOException {
-        super(size,idSize,foffsetSize,foffsetSize + 4 + 1 + idSize + foffsetSize);
-        referenceList = new NumberList(ID_SIZE);
+    LongMap(int size,int idSize,int foffsetSize,CacheDirectory cacheDir) throws FileNotFoundException, IOException {
+        super(size,idSize,foffsetSize,foffsetSize + 4 + 1 + idSize + foffsetSize, cacheDir);
+        referenceList = new NumberList(ID_SIZE, cacheDir);
     }
 
     //~ Methods ------------------------------------------------------------------------------------------------------------------
@@ -290,4 +290,15 @@ void flush() {
         }
         return bigIds;
     }
+
+    //---- Serialization support    
+    void writeToStream(DataOutputStream out) throws IOException {
+        super.writeToStream(out);
+        referenceList.writeToStream(out);
+    }
+    
+    LongMap(DataInputStream dis, CacheDirectory cacheDir) throws IOException {
+        super(dis, cacheDir);
+        referenceList = new NumberList(dis, cacheDir);
+    }
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/NearestGCRoot.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/NearestGCRoot.java
index e5448eb59..e7e3aca38 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/NearestGCRoot.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/NearestGCRoot.java
@@ -19,6 +19,8 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -42,6 +44,8 @@
     };
     private static final String JAVA_LANG_REF_REFERENCE = "java.lang.ref.Reference";   // NOI18N
     private static final String REFERENT_FILED_NAME = "referent"; // NOI18N
+    private static final String SVM_REFFERENCE = "com.oracle.svm.core.heap.heapImpl.DiscoverableReference";    // NOI18N
+    private static final String SVM_REFERENT_FILED_NAME = "rawReferent"; // NOI18N
     
     //~ Instance fields ----------------------------------------------------------------------------------------------------------
 
@@ -86,15 +90,12 @@ private synchronized void computeGCRoots() {
         if (gcRootsComputed) {
             return;
         }
-        referenceClasses = new HashSet();
-        for (int i=0; i<REF_CLASSES.length; i++) {
-            JavaClass ref = heap.getJavaClassByName(REF_CLASSES[i]);
-            if (ref != null) {
-                referenceClasses.add(ref);
-                referenceClasses.addAll(ref.getSubClasses());
+        HeapProgress.progressStart();
+        if (!initHotSpotReference()) {
+            if (!initSVMReference()) {
+                throw new IllegalArgumentException("reference field not found"); // NOI18N
             }
         }
-        referentFiled = computeReferentFiled();
         heap.computeReferences(); // make sure references are computed first
         allInstances = heap.getSummary().getTotalLiveInstances();
         Set processedClasses = new HashSet(heap.getAllClasses().size()*4/3);
@@ -113,8 +114,38 @@ private synchronized void computeGCRoots() {
 
         deleteBuffers();
         heap.idToOffsetMap.flush();
-        HeapProgress.progressFinish();
         gcRootsComputed = true;
+        heap.writeToFile();
+        HeapProgress.progressFinish();
+    }
+
+    private boolean initHotSpotReference() {
+        referentFiled = computeReferentFiled(JAVA_LANG_REF_REFERENCE, REFERENT_FILED_NAME);
+        if (referentFiled != null) {
+            referenceClasses = new HashSet();
+            for (int i=0; i<REF_CLASSES.length; i++) {
+                JavaClass ref = heap.getJavaClassByName(REF_CLASSES[i]);
+                if (ref != null) {
+                    referenceClasses.add(ref);
+                    referenceClasses.addAll(ref.getSubClasses());
+                }
+            }
+            return referenceClasses.size() >= REF_CLASSES.length;
+        }
+        return false;
+    }
+
+    private boolean initSVMReference() {
+        referentFiled = computeReferentFiled(SVM_REFFERENCE, SVM_REFERENT_FILED_NAME);
+        if (referentFiled != null) {
+            JavaClass ref = referentFiled.getDeclaringClass();
+
+            referenceClasses = new HashSet();
+            referenceClasses.add(ref);
+            referenceClasses.addAll(ref.getSubClasses());
+            return !referenceClasses.isEmpty();
+        }
+        return false;
     }
 
     private void computeOneLevel(Set processedClasses) throws IOException {
@@ -195,27 +226,29 @@ private void computeOneLevel(Set processedClasses) throws IOException {
         }
     }
 
-    private Field computeReferentFiled() {
-        JavaClass reference = heap.getJavaClassByName(JAVA_LANG_REF_REFERENCE);
-        Iterator fieldRef = reference.getFields().iterator();
+    private Field computeReferentFiled(String className, String fieldName) {
+        JavaClass reference = heap.getJavaClassByName(className);
+
+        if (reference != null) {
+            Iterator fieldRef = reference.getFields().iterator();
 
-        while (fieldRef.hasNext()) {
-            Field f = (Field) fieldRef.next();
+            while (fieldRef.hasNext()) {
+                Field f = (Field) fieldRef.next();
 
-            if (f.getName().equals(REFERENT_FILED_NAME)) {
+                if (f.getName().equals(fieldName)) {
 
-                return f;
+                    return f;
+                }
             }
         }
-
-        throw new IllegalArgumentException("reference field not found in " + reference.getName()); // NOI18N
+        return null;
     }
 
     private void createBuffers() {
-        readBuffer = new LongBuffer(BUFFER_SIZE);
-        writeBuffer = new LongBuffer(BUFFER_SIZE);
-        leaves = new LongBuffer(BUFFER_SIZE);
-        multipleParents = new LongBuffer(BUFFER_SIZE);
+        readBuffer = new LongBuffer(BUFFER_SIZE, heap.cacheDirectory);
+        writeBuffer = new LongBuffer(BUFFER_SIZE, heap.cacheDirectory);
+        leaves = new LongBuffer(BUFFER_SIZE, heap.cacheDirectory);
+        multipleParents = new LongBuffer(BUFFER_SIZE, heap.cacheDirectory);
     }
 
     private void deleteBuffers() {
@@ -349,4 +382,22 @@ LongBuffer getMultipleParents() {
         computeGCRoots();
         return multipleParents;
     }
+
+    //---- Serialization support
+    void writeToStream(DataOutputStream out) throws IOException {
+        out.writeBoolean(gcRootsComputed);
+        if (gcRootsComputed) {
+            leaves.writeToStream(out);
+            multipleParents.writeToStream(out);
+        }
+    }
+
+    NearestGCRoot(HprofHeap h, DataInputStream dis) throws IOException {
+        this(h);
+        gcRootsComputed = dis.readBoolean();
+        if (gcRootsComputed) {
+            leaves = new LongBuffer(dis, heap.cacheDirectory);
+            multipleParents = new LongBuffer(dis, heap.cacheDirectory);
+        }
+    }
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/NumberList.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/NumberList.java
index 1bcb864bc..67ccb2470 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/NumberList.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/NumberList.java
@@ -19,6 +19,8 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -30,6 +32,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 /**
@@ -47,19 +50,20 @@
     private long blocks;
     private MappedByteBuffer buf;
     private long mappedSize;
+    private CacheDirectory cacheDirectory;
     
-    NumberList(long dumpFileSize) throws IOException {
-        this(bytes(dumpFileSize));
+    NumberList(long dumpFileSize, CacheDirectory cacheDir) throws IOException {
+        this(bytes(dumpFileSize), cacheDir);
     }
     
-    NumberList(int elSize) throws IOException {
-        dataFile = File.createTempFile("NBProfiler", ".ref"); // NOI18N
+    NumberList(int elSize, CacheDirectory cacheDir) throws IOException {
+        dataFile = cacheDir.createTempFile("NBProfiler", ".ref"); // NOI18N
         data = new RandomAccessFile(dataFile, "rw"); // NOI18N
         numberSize = elSize;
         blockCache = new BlockLRUCache();
         dirtyBlocks = new HashSet(100000);
         blockSize = (NUMBERS_IN_BLOCK + 1) * numberSize;
-        dataFile.deleteOnExit();
+        cacheDirectory = cacheDir;
         addBlock(); // first block is unused, since it starts at offset 0
     }
      
@@ -89,7 +93,9 @@ private static int bytes(long number) {
     }
     
     protected void finalize() throws Throwable {
-        dataFile.delete();
+        if (cacheDirectory.isTemporary()) {
+            dataFile.delete();
+        }
         super.finalize();
     }
     
@@ -155,6 +161,10 @@ long getFirstNumber(long startOffset) throws IOException {
         return readNumber(block,0);
     }
     
+    LongIterator getNumbersIterator(long startOffset) throws IOException {
+        return new NumberIterator(startOffset);
+    }
+
     List getNumbers(long startOffset) throws IOException {
         int slot;
         List numbers = new ArrayList();
@@ -316,7 +326,89 @@ private void flushDirtyBlocks() throws IOException {
         data.write(blocks,0,dataOffset);
         dirtyBlocks.clear();
     }
+
+    //---- Serialization support
+    void writeToStream(DataOutputStream out) throws IOException {
+        out.writeUTF(dataFile.getAbsolutePath());
+        out.writeInt(numberSize);
+        out.writeLong(blocks);
+        out.writeBoolean(buf != null);        
+    }
+
+    NumberList(DataInputStream dis, CacheDirectory cacheDir) throws IOException {
+        boolean mmaped;
+        
+        cacheDirectory = cacheDir;
+        dataFile = cacheDirectory.getCacheFile(dis.readUTF());
+        data = new RandomAccessFile(dataFile, "rw"); // NOI18N
+        numberSize = dis.readInt();
+        blocks = dis.readLong();
+        mmaped = dis.readBoolean();
+        blockCache = new BlockLRUCache();
+        dirtyBlocks = new HashSet(100000);
+        blockSize = (NUMBERS_IN_BLOCK + 1) * numberSize;
+        if (mmaped) {
+            mmapData();
+        }
+    }    
     
+    private class NumberIterator extends LongIterator {
+        private int slot;
+        private byte[] block;
+        private long nextNumber;
+
+        private NumberIterator(long startOffset) throws IOException {
+            slot = 0;
+            block = getBlock(startOffset);
+            nextNumber();
+        }
+
+        @Override
+        boolean hasNext() {
+            return nextNumber != 0;
+        }
+
+        @Override
+        long next() {
+            if (hasNext()) {
+                long num = nextNumber;
+                try {
+                    nextNumber();
+                } catch (IOException ex) {
+                    ex.printStackTrace();
+                    nextNumber = 0;
+                }
+                return num;
+            }
+            throw new NoSuchElementException();
+        }
+
+        private void nextNumber() throws IOException {
+            if (slot < NUMBERS_IN_BLOCK) {
+                long nextNum = readNumber(block,slot++);
+                if (nextNum == 0) {     // end of the block, move to next one
+                    nextBlock();
+                } else {
+                    nextNumber = nextNum;
+                }
+            } else {
+               nextBlock();
+            }
+        }
+
+        private void nextBlock() throws IOException {
+            long nextBlock = getOffsetToNextBlock(block);
+
+            if (nextBlock == 0) { // end of list
+                nextNumber = 0;
+                return;
+            }
+            block = getBlock(nextBlock);
+            slot = 0;
+            nextNumber();
+        }
+    }
+
     private class BlockLRUCache extends LinkedHashMap {
         
         private static final int MAX_CAPACITY = 10000;
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayDump.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayDump.java
index 5fd185472..ed89f910e 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayDump.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayDump.java
@@ -47,6 +47,17 @@ public long getSize() {
 
         return new ObjectArrayLazyList(heap, dumpBuffer, getLength(), getOffset());
     }
+    
+    public List /*<ArrayItemValue>*/ getItems() {
+        return new ObjectArrayValuesLazyList(dumpClass, getLength(), fileOffset);
+//        List items = new ArrayList();
+//        
+//        int length = getLength();
+//        for (int i = 0; i < length; i++)
+//            items.add(new HprofArrayValue(dumpClass, fileOffset, i));
+//        
+//        return items;
+    }
 
     long getOffset() {
         int idSize = dumpClass.getHprofBuffer().getIDSize();
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayInstance.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayInstance.java
index b09dfc7e2..eeedfc8ea 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayInstance.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayInstance.java
@@ -45,4 +45,7 @@
      * @return list {@link Instance} of elements.
      */
     List /*<Instance>*/ getValues();
+    
+    List /*<ArrayItemValue>*/ getItems();
+    
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayValuesLazyList.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayValuesLazyList.java
new file mode 100644
index 000000000..e428c4d6c
--- /dev/null
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/ObjectArrayValuesLazyList.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.lib.profiler.heap;
+
+import java.util.AbstractList;
+
+
+/**
+ *
+ * @author Jiri Sedlacek
+ */
+class ObjectArrayValuesLazyList extends AbstractList {
+    //~ Instance fields ----------------------------------------------------------------------------------------------------------
+
+    private final ClassDump dumpClass;
+    private final int length;
+    private final long offset;
+
+    //~ Constructors -------------------------------------------------------------------------------------------------------------
+
+    ObjectArrayValuesLazyList(ClassDump dump, int len, long off) {
+        dumpClass = dump;
+        length = len;
+        offset = off;
+    }
+
+    //~ Methods ------------------------------------------------------------------------------------------------------------------
+
+    public Object get(int index) {
+        return new HprofArrayValue(dumpClass, offset, index);
+    }
+
+    public int size() {
+        return length;
+    }
+}
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/SyntheticClassObjectValue.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/SyntheticClassObjectValue.java
index c4925d657..0788ab87f 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/SyntheticClassObjectValue.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/SyntheticClassObjectValue.java
@@ -26,11 +26,9 @@
  */
 class SyntheticClassObjectValue extends HprofInstanceObjectValue {
     //~ Constructors -------------------------------------------------------------------------------------------------------------
-    Field syntheticField;
 
-    SyntheticClassObjectValue(InstanceDump i, Field f, long offset) {
-        super(i, null, offset);
-        syntheticField = f;
+    SyntheticClassObjectValue(InstanceDump i, HprofField f, long fieldOffset) {
+        super(i, f, fieldOffset);
     }
 
     //~ Methods ------------------------------------------------------------------------------------------------------------------
@@ -39,15 +37,11 @@ Object getTypeValue() {
         return new Long(getInstanceId());
     }
 
-    public Field getField() {
-        return syntheticField;
-    }
-
     public Instance getInstance() {
-        return new ClassDumpInstance(instance.dumpClass);
+        return new ClassDumpInstance(field.classDump);
     }
 
     long getInstanceId() {
-        return instance.dumpClass.getJavaClassId();
+        return field.classDump.getJavaClassId();
     }
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/TagBounds.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/TagBounds.java
index 869d4ed7d..bb0623249 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/TagBounds.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/TagBounds.java
@@ -19,6 +19,10 @@
 
 package org.netbeans.lib.profiler.heap;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import static org.netbeans.lib.profiler.heap.HprofHeap.*;
 
 /**
  *
@@ -51,4 +55,57 @@ TagBounds union(TagBounds otherTagBounds) {
 
         return new TagBounds(-1, start, end);
     }
+
+    //---- Serialization support
+    void writeToStream(DataOutputStream out) throws IOException {
+        out.writeInt(tag);
+        out.writeLong(startOffset);
+        out.writeLong(endOffset);
+    }
+
+    TagBounds(DataInputStream dis) throws IOException {
+        tag = dis.readInt();
+        startOffset = dis.readLong();
+        endOffset = dis.readLong();
+    }
+    
+    static void writeToStream(TagBounds[] bounds, DataOutputStream out) throws IOException {
+        int tags = 0;
+        for (int i = 0; i < bounds.length; i++) {
+            if (bounds[i] != null) {
+                tags++;
+            }
+        }
+        out.writeInt(tags);
+        for (int i = 0; i < bounds.length; i++) {
+            if (bounds[i] != null) {
+                bounds[i].writeToStream(out);
+            }
+        }
+    }
+
+    static void readFromStream(DataInputStream dis, HprofHeap heap, TagBounds[] heapTagBounds) throws IOException {
+        int tags = dis.readInt();
+        for (int i = 0; i<tags; i++) {
+            int tag = dis.readInt();
+            long startOffset = dis.readLong();
+            long endOffset = dis.readLong();
+            TagBounds newBounds;
+                        
+            if (tag == LOAD_CLASS) {
+                newBounds = new LoadClassSegment(heap, startOffset, endOffset);
+            } else if (tag == STRING) {
+                newBounds = new StringSegment(heap, startOffset, endOffset);
+            } else if (tag == STACK_TRACE) {
+                newBounds = new StackTraceSegment(heap, startOffset, endOffset);
+            } else if (tag == STACK_FRAME) {
+                newBounds = new StackFrameSegment(heap, startOffset, endOffset);
+            } else if (tag == CLASS_DUMP) {
+                newBounds = new ClassDumpSegment(heap, startOffset, endOffset, dis);
+            } else {
+                newBounds = new TagBounds(tag, startOffset, endOffset);
+            }
+            heapTagBounds[newBounds.tag] = newBounds;
+        }
+    }
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/heap/TreeObject.java b/lib.profiler/src/org/netbeans/lib/profiler/heap/TreeObject.java
index be9122c1f..2c1a2efdc 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/heap/TreeObject.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/heap/TreeObject.java
@@ -150,7 +150,7 @@ private boolean processInstance(Instance instance, long size, long retainedSize)
     }
     
     private void createBuffers() {
-        readBuffer = new LongBuffer(BUFFER_SIZE);
+        readBuffer = new LongBuffer(BUFFER_SIZE, heap.cacheDirectory);
     }
     
     private void deleteBuffers() {
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor1.java b/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor1.java
index 222b0be0e..2c3e405e7 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor1.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor1.java
@@ -398,6 +398,7 @@ private void checkAndScanMethod(String className, int classLoaderId, String meth
     private boolean locateAndMarkMethodReachable(DynamicClassInfo clazz, String methodName, String methodSignature,
                                                  boolean virtualCall, boolean lookupInSuperIfNotFoundInThis,
                                                  boolean checkSubclasses, boolean setAsMarkerMethod) {
+        boolean constructorNotInstrumented = false;
         if (clazz == null) {
             return false; // Normally shouldn't happen, it's just development-time facilitation (introduced when working on 1.5 support)
         }
@@ -421,6 +422,9 @@ private boolean locateAndMarkMethodReachable(DynamicClassInfo clazz, String meth
                     || (!clazz.isMethodRoot(idx) && !clazz.isMethodMarker(idx) && !instrFilter.passes(className))
                     || (className == OBJECT_SLASHED_CLASS_NAME)) {  // Actually, just the Object.<init> method?
                 clazz.setMethodUnscannable(idx);
+            } else if (methodName == "<init>" && !status.canInstrumentConstructor && clazz.getMajorVersion()>50) {
+                clazz.setMethodUnscannable(idx);
+                constructorNotInstrumented = true;
             } else {
                 byte[] bytecode = clazz.getMethodBytecode(idx);
 
@@ -444,6 +448,8 @@ private boolean locateAndMarkMethodReachable(DynamicClassInfo clazz, String meth
                 if (setAsMarkerMethod) {
                     clazz.setMethodMarker(idx);
                 }
+            } else if (constructorNotInstrumented) {
+                scanMethod(clazz, idx);
             }
         }
 
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor2.java b/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor2.java
index bc95236e5..a93270149 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor2.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor2.java
@@ -346,6 +346,7 @@ protected boolean tryInstrumentSpawnedThreads(DynamicClassInfo clazz) {
      */
     private boolean checkAndScanMethod(DynamicClassInfo clazz, String methodName, String methodSignature, boolean virtualCall,
                                        boolean lookupInSuperIfNotFoundInThis, boolean checkSubclasses) {
+        boolean constructorNotInstrumented = false;
         String className = clazz.getName();
         int idx = clazz.getMethodIndex(methodName, methodSignature);
 
@@ -364,6 +365,9 @@ private boolean checkAndScanMethod(DynamicClassInfo clazz, String methodName, St
                         || (!clazz.isMethodRoot(idx) && !clazz.isMethodMarker(idx) && !instrFilter.passes(className))
                         || (className == OBJECT_SLASHED_CLASS_NAME)) {  // Actually, just the Object.<init> method?
                     clazz.setMethodUnscannable(idx);
+                } else if (methodName == "<init>" && !status.canInstrumentConstructor && clazz.getMajorVersion()>50) {
+                    clazz.setMethodUnscannable(idx);
+                    constructorNotInstrumented = true;
                 } else {
                     bytecode = clazz.getMethodBytecode(idx);
 
@@ -395,6 +399,8 @@ private boolean checkAndScanMethod(DynamicClassInfo clazz, String methodName, St
                 clazz.setMethodScanned(idx);
                 //if (!lookupInSuperIfNotFoundInThis && !checkSubclasses) System.out.println("Gonna scan potentially reachable " + className + "." + methodName + methodSignature);
                 scanBytecode(clazz, bytecode);
+            } else if (constructorNotInstrumented) {
+                scanBytecode(clazz, bytecode);
             }
         }
 
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor3.java b/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor3.java
index 80cc543f7..74a40a939 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor3.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/instrumentation/RecursiveMethodInstrumentor3.java
@@ -310,6 +310,8 @@ private void checkAndMarkMethodForInstrumentation(DynamicClassInfo clazz, int id
                     || (className == OBJECT_SLASHED_CLASS_NAME) // Actually, just the Object.<init> method?
             ) {
                 clazz.setMethodUnscannable(idx);
+            } else if (clazz.getMethodName(idx) == "<init>" && !status.canInstrumentConstructor && clazz.getMajorVersion()>50) {
+                clazz.setMethodUnscannable(idx);
             } else {
                 byte[] bytecode = clazz.getMethodBytecode(idx);
 
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/results/CCTNode.java b/lib.profiler/src/org/netbeans/lib/profiler/results/CCTNode.java
index 501d01f1f..943162611 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/results/CCTNode.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/results/CCTNode.java
@@ -94,4 +94,12 @@ public TreeNode getChildAt(int index) {
         return getChild(index);
     }
     //---
+    
+    
+    public static interface FixedPosition {}
+    
+    public static interface AlwaysFirst extends FixedPosition {}
+    
+    public static interface AlwaysLast extends FixedPosition {}
+    
 }
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/server/InstrumentConstructorTest.java b/lib.profiler/src/org/netbeans/lib/profiler/server/InstrumentConstructorTest.java
new file mode 100644
index 000000000..201b4a76e
--- /dev/null
+++ b/lib.profiler/src/org/netbeans/lib/profiler/server/InstrumentConstructorTest.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.lib.profiler.server;
+
+/**
+ *
+ * @author Tomas Hurka
+ */
+class InstrumentConstructorTest {
+
+    private static final boolean DEBUG = Boolean.getBoolean("org.netbeans.lib.profiler.server.InstrumentConstructorTest");
+
+    static boolean test() {
+        try {
+            return new TestClassLoader().test();
+        } catch (InstantiationException ex) {
+            ex.printStackTrace();
+        } catch (IllegalAccessException ex) {
+            ex.printStackTrace();
+        }
+        return false;
+    }
+
+    private static class TestClassLoader extends ClassLoader {
+    /*
+        Classfile org/netbeans/lib/profiler/ConstructorTest.class
+          Last modified Sep 25, 2017; size 591 bytes
+          MD5 checksum a79cf0de0d4b40876d3329998b138a70
+          Compiled from "ConstructorTest.java"
+        public class org.netbeans.lib.profiler.ConstructorTest
+          minor version: 0
+          major version: 51
+          flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+          this_class: #2                          // org/netbeans/lib/profiler/ConstructorTest
+          super_class: #3                         // java/lang/Object
+          interfaces: 0, fields: 0, methods: 1, attributes: 1
+        Constant pool:
+           #1 = Methodref          #3.#13         // java/lang/Object."<init>":()V
+           #2 = Class              #14            // org/netbeans/lib/profiler/ConstructorTest
+           #3 = Class              #15            // java/lang/Object
+           #4 = Utf8               <init>
+           #5 = Utf8               ()V
+           #6 = Utf8               Code
+           #7 = Utf8               LineNumberTable
+           #8 = Utf8               LocalVariableTable
+           #9 = Utf8               this
+          #10 = Utf8               Lorg/netbeans/lib/profiler/ConstructorTest;
+          #11 = Utf8               SourceFile
+          #12 = Utf8               ConstructorTest.java
+          #13 = NameAndType        #4:#5          // "<init>":()V
+          #14 = Utf8               org/netbeans/lib/profiler/ConstructorTest
+          #15 = Utf8               java/lang/Object
+          #16 = Methodref          #17.#19        // org/netbeans/lib/profiler/server/ProfilerRuntimeCPUFullInstr.methodEntry:(C)V
+          #17 = Class              #18            // org/netbeans/lib/profiler/server/ProfilerRuntimeCPUFullInstr
+          #18 = Utf8               org/netbeans/lib/profiler/server/ProfilerRuntimeCPUFullInstr
+          #19 = NameAndType        #20:#21        // methodEntry:(C)V
+          #20 = Utf8               methodEntry
+          #21 = Utf8               (C)V
+          #22 = Methodref          #17.#23        // org/netbeans/lib/profiler/server/ProfilerRuntimeCPUFullInstr.methodExit:(C)V
+          #23 = NameAndType        #24:#21        // methodExit:(C)V
+          #24 = Utf8               methodExit
+          #25 = Methodref          #17.#26        // org/netbeans/lib/profiler/server/ProfilerRuntimeCPUFullInstr.profilePointHit:(C)V
+          #26 = NameAndType        #27:#21        // profilePointHit:(C)V
+          #27 = Utf8               profilePointHit
+          #28 = Methodref          #17.#29        // org/netbeans/lib/profiler/server/ProfilerRuntimeCPUFullInstr.rootMethodEntry:(C)V
+          #29 = NameAndType        #30:#21        // rootMethodEntry:(C)V
+          #30 = Utf8               rootMethodEntry
+          #31 = Utf8               StackMapTable
+          #32 = Class              #33            // java/lang/Throwable
+          #33 = Utf8               java/lang/Throwable
+        {
+          public org.netbeans.lib.profiler.ConstructorTest();
+            descriptor: ()V
+            flags: (0x0001) ACC_PUBLIC
+            Code:
+              stack=4, locals=2, args_size=1
+                 0: sipush        1
+                 3: invokestatic  #28                 // Method org/netbeans/lib/profiler/server/ProfilerRuntimeCPUFullInstr.rootMethodEntry:(C)V
+                 6: nop
+                 7: nop
+                 8: aload_0
+                 9: invokespecial #1                  // Method java/lang/Object."<init>":()V
+                12: sipush        1
+                15: invokestatic  #22                 // Method org/netbeans/lib/profiler/server/ProfilerRuntimeCPUFullInstr.methodExit:(C)V
+                18: nop
+                19: nop
+                20: return
+                21: astore_1
+                22: sipush        1
+                25: invokestatic  #22                 // Method org/netbeans/lib/profiler/server/ProfilerRuntimeCPUFullInstr.methodExit:(C)V
+                28: aload_1
+                29: athrow
+              Exception table:
+                 from    to  target type
+                     0    21    21   any
+              LineNumberTable:
+                line 12: 0
+              LocalVariableTable:
+                Start  Length  Slot  Name   Signature
+                    8      13     0  this   Lorg/netbeans/lib/profiler/ConstructorTest;
+              StackMapTable: number_of_entries = 1
+                frame_type = 255 // full_frame
+                  offset_delta = 21
+                  locals = [ top ]
+                  stack = [ class java/lang/Throwable ]
+        }
+        SourceFile: "ConstructorTest.java"
+        */
+        // od -t u1 ConstructorTest.class | awk '{for (i=2; i<=NF; i++) { val=$i; if (val>127) val-=256 ; printf ("%d, ", val); } printf "\n"}'
+
+        private byte[] classBytes = new byte[]{-54, -2, -70, -66, 0, 0, 0, 51, 0, 34, 10, 0, 3, 0, 13, 7,
+            0, 14, 7, 0, 15, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0,
+            3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105,
+            110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18,
+            76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98,
+            108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 43, 76, 111, 114, 103,
+            47, 110, 101, 116, 98, 101, 97, 110, 115, 47, 108, 105, 98, 47, 112, 114,
+            111, 102, 105, 108, 101, 114, 47, 67, 111, 110, 115, 116, 114, 117, 99, 116,
+            111, 114, 84, 101, 115, 116, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101,
+            70, 105, 108, 101, 1, 0, 20, 67, 111, 110, 115, 116, 114, 117, 99, 116,
+            111, 114, 84, 101, 115, 116, 46, 106, 97, 118, 97, 12, 0, 4, 0, 5,
+            1, 0, 41, 111, 114, 103, 47, 110, 101, 116, 98, 101, 97, 110, 115, 47,
+            108, 105, 98, 47, 112, 114, 111, 102, 105, 108, 101, 114, 47, 67, 111, 110,
+            115, 116, 114, 117, 99, 116, 111, 114, 84, 101, 115, 116, 1, 0, 16, 106,
+            97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 10,
+            0, 17, 0, 19, 7, 0, 18, 1, 0, 60, 111, 114, 103, 47, 110, 101,
+            116, 98, 101, 97, 110, 115, 47, 108, 105, 98, 47, 112, 114, 111, 102, 105,
+            108, 101, 114, 47, 115, 101, 114, 118, 101, 114, 47, 80, 114, 111, 102, 105,
+            108, 101, 114, 82, 117, 110, 116, 105, 109, 101, 67, 80, 85, 70, 117, 108,
+            108, 73, 110, 115, 116, 114, 12, 0, 20, 0, 21, 1, 0, 11, 109, 101,
+            116, 104, 111, 100, 69, 110, 116, 114, 121, 1, 0, 4, 40, 67, 41, 86,
+            10, 0, 17, 0, 23, 12, 0, 24, 0, 21, 1, 0, 10, 109, 101, 116,
+            104, 111, 100, 69, 120, 105, 116, 10, 0, 17, 0, 26, 12, 0, 27, 0,
+            21, 1, 0, 15, 112, 114, 111, 102, 105, 108, 101, 80, 111, 105, 110, 116,
+            72, 105, 116, 10, 0, 17, 0, 29, 12, 0, 30, 0, 21, 1, 0, 15,
+            114, 111, 111, 116, 77, 101, 116, 104, 111, 100, 69, 110, 116, 114, 121, 1,
+            0, 13, 83, 116, 97, 99, 107, 77, 97, 112, 84, 97, 98, 108, 101, 7,
+            0, 33, 1, 0, 19, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 84,
+            104, 114, 111, 119, 97, 98, 108, 101, 0, 33, 0, 2, 0, 3, 0, 0,
+            0, 0, 0, 1, 0, 1, 0, 4, 0, 5, 0, 1, 0, 6, 0, 0,
+            0, 99, 0, 4, 0, 2, 0, 0, 0, 30, 17, 0, 1, -72, 0, 28,
+            0, 0, 42, -73, 0, 1, 17, 0, 1, -72, 0, 22, 0, 0, -79, 76,
+            17, 0, 1, -72, 0, 22, 43, -65, 0, 1, 0, 0, 0, 21, 0, 21,
+            0, 0, 0, 3, 0, 7, 0, 0, 0, 6, 0, 1, 0, 0, 0, 12,
+            0, 8, 0, 0, 0, 12, 0, 1, 0, 8, 0, 13, 0, 9, 0, 10,
+            0, 0, 0, 31, 0, 0, 0, 13, 0, 1, -1, 0, 21, 0, 1, 0,
+            0, 1, 7, 0, 32, 0, 1, 0, 11, 0, 0, 0, 2, 0, 12,};
+
+        private boolean test() throws InstantiationException, IllegalAccessException {
+            if (DEBUG) {
+                System.err.println("ConstructorTest Class size:"+classBytes.length);
+            }
+            Class cls = defineClass("org.netbeans.lib.profiler.ConstructorTest", classBytes, 0, classBytes.length);
+            if (DEBUG) {
+                System.err.println("ConstructorTest Class load:"+cls);
+            }
+            try {
+                cls.getConstructors();
+            } catch (VerifyError ve) {
+                if (DEBUG) {
+                    ve.printStackTrace();
+                }
+                return false;
+            }
+            return true;
+        }
+    }
+
+}
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/server/ProfilerServer.java b/lib.profiler/src/org/netbeans/lib/profiler/server/ProfilerServer.java
index effb5ea64..724b4a227 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/server/ProfilerServer.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/server/ProfilerServer.java
@@ -1582,6 +1582,7 @@ public void run() {
                                                                      System.getProperty("user.dir"), // NOI18N
                                                                      status.jvmArguments, status.javaCommand,
                                                                      System.getProperty("os.name"), // NOI18N
+                                                                     InstrumentConstructorTest.test(),
                                                                      Runtime.getRuntime().maxMemory(),
                                                                      System.currentTimeMillis(), Timers.getCurrentTimeInCounts(),
                                                                      getAgentId()
diff --git a/lib.profiler/src/org/netbeans/lib/profiler/wireprotocol/VMPropertiesResponse.java b/lib.profiler/src/org/netbeans/lib/profiler/wireprotocol/VMPropertiesResponse.java
index e08ab5b2a..a1c0dcd19 100644
--- a/lib.profiler/src/org/netbeans/lib/profiler/wireprotocol/VMPropertiesResponse.java
+++ b/lib.profiler/src/org/netbeans/lib/profiler/wireprotocol/VMPropertiesResponse.java
@@ -43,6 +43,7 @@
     private String jvmArguments;
     private String targetMachineOSName;
     private String workingDir;
+    private boolean canInstrumentConstructor;
     private int agentId;
     private int agentVersion;
     private long maxHeapSize;
@@ -53,7 +54,8 @@
 
     public VMPropertiesResponse(String jdkVerString, String javaClassPath, String javaExtDirs, String bootClassPath,
                                 String workingDir, String jvmArguments, String javaCommand, String targetMachineOSName,
-                                long maxHeapSize, long startupTimeMillis, long startupTimeInCounts, int agentId) {
+                                boolean canInstrumentConstructor, long maxHeapSize, long startupTimeMillis,
+                                long startupTimeInCounts, int agentId) {
         super(true, VM_PROPERTIES);
         this.jdkVersionString = jdkVerString;
         this.javaClassPath = javaClassPath;
@@ -63,6 +65,7 @@ public VMPropertiesResponse(String jdkVerString, String javaClassPath, String ja
         this.jvmArguments = (jvmArguments != null) ? jvmArguments : ""; // NOI18N
         this.javaCommand = (javaCommand != null) ? javaCommand : ""; // NOI18N
         this.targetMachineOSName = targetMachineOSName;
+        this.canInstrumentConstructor = canInstrumentConstructor;
         this.maxHeapSize = maxHeapSize;
         this.startupTimeMillis = startupTimeMillis;
         this.startupTimeInCounts = startupTimeInCounts & 0xFFFFFFFFFFFFFFL; // we use only 7 bytes for hi res timer
@@ -109,6 +112,10 @@ public String getJavaExtDirs() {
         return javaExtDirs;
     }
 
+    public boolean canInstrumentConstructor() {
+        return canInstrumentConstructor;
+    }
+
     public long getMaxHeapSize() {
         return maxHeapSize;
     }
@@ -140,6 +147,7 @@ public String toString() {
                + "\n  jvmArguments: " + jvmArguments // NOI18N
                + "\n  javaCommand: " + javaCommand // NOI18N
                + "\n  targetMachineOSName: " + targetMachineOSName // NOI18N
+               + "\n  canInstrumentConstructor: " + canInstrumentConstructor // NOI18N
                + "\n  maxHeapSize: " + maxHeapSize // NOI18N
                + "\n  startupTimeMillis: " + startupTimeMillis // NOI18N
                + "\n  startupTimeInCounts: " + startupTimeInCounts // NOI18N
@@ -158,6 +166,7 @@ void readObject(ObjectInputStream in) throws IOException {
         jvmArguments = in.readUTF();
         javaCommand = in.readUTF();
         targetMachineOSName = in.readUTF();
+        canInstrumentConstructor = in.readBoolean();
         maxHeapSize = in.readLong();
         startupTimeMillis = in.readLong();
         startupTimeInCounts = in.readLong();
@@ -174,6 +183,7 @@ void writeObject(ObjectOutputStream out) throws IOException {
         out.writeUTF(jvmArguments);
         out.writeUTF(javaCommand);
         out.writeUTF(targetMachineOSName);
+        out.writeBoolean(canInstrumentConstructor);
         out.writeLong(maxHeapSize);
         out.writeLong(startupTimeMillis);
         out.writeLong(startupTimeInCounts);
diff --git a/profiler.api/manifest.mf b/profiler.api/manifest.mf
index 545d6cd26..be70a6349 100644
--- a/profiler.api/manifest.mf
+++ b/profiler.api/manifest.mf
@@ -1,5 +1,5 @@
 Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.modules.profiler.api/1
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/profiler/api/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.47
+OpenIDE-Module-Specification-Version: 1.51
 Netigso-Export-Package: org.netbeans.modules.profiler.spi
diff --git a/profiler.api/nbproject/project.xml b/profiler.api/nbproject/project.xml
index f8651ba7e..f2e91a47c 100644
--- a/profiler.api/nbproject/project.xml
+++ b/profiler.api/nbproject/project.xml
@@ -103,8 +103,13 @@
                 </dependency>
             </module-dependencies>
             <friend-packages>
+                <friend>com.sun.tools.visualvm.application.views</friend>
                 <friend>com.sun.tools.visualvm.charts</friend>
                 <friend>com.sun.tools.visualvm.core</friend>
+                <friend>com.sun.tools.visualvm.heapdump</friend>
+                <friend>com.sun.tools.visualvm.heapviewer</friend>
+                <friend>com.sun.tools.visualvm.heapviewer.console</friend>
+                <friend>com.sun.tools.visualvm.heapviewer.truffle</friend>
                 <friend>com.sun.tools.visualvm.modules.appui</friend>
                 <friend>com.sun.tools.visualvm.profiler</friend>
                 <friend>com.sun.tools.visualvm.profiling</friend>
diff --git a/profiler.heapwalker/manifest.mf b/profiler.heapwalker/manifest.mf
index e478b75ac..5f9f06e72 100644
--- a/profiler.heapwalker/manifest.mf
+++ b/profiler.heapwalker/manifest.mf
@@ -2,5 +2,5 @@ Manifest-Version: 1.0
 AutoUpdate-Show-In-Client: false
 OpenIDE-Module: org.netbeans.modules.profiler.heapwalker
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/profiler/heapwalker/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.107
+OpenIDE-Module-Specification-Version: 1.113
 
diff --git a/profiler.heapwalker/nbproject/project.properties b/profiler.heapwalker/nbproject/project.properties
index c0af75f0c..0f4ec98bc 100644
--- a/profiler.heapwalker/nbproject/project.properties
+++ b/profiler.heapwalker/nbproject/project.properties
@@ -14,5 +14,5 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-javac.source=1.6
+javac.source=1.8
 javac.compilerargs=-Xlint -Xlint:-serial
diff --git a/profiler.heapwalker/nbproject/project.xml b/profiler.heapwalker/nbproject/project.xml
index 2ebc2a085..b3325708e 100644
--- a/profiler.heapwalker/nbproject/project.xml
+++ b/profiler.heapwalker/nbproject/project.xml
@@ -40,7 +40,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>1</release-version>
-                        <specification-version>1.103</specification-version>
+                        <specification-version>1.106</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -58,7 +58,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>1</release-version>
-                        <specification-version>1.33</specification-version>
+                        <specification-version>1.142</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -163,9 +163,12 @@
             </module-dependencies>
             <friend-packages>
                 <friend>com.sun.tools.visualvm.heapdump</friend>
+                <friend>com.sun.tools.visualvm.heapviewer</friend>
+                <friend>com.sun.tools.visualvm.heapviewer.truffle</friend>
                 <friend>oracle.jdevimpl.profiler.impl</friend>
                 <friend>org.netbeans.modules.debugger.jpda.heapwalk</friend>
                 <package>org.netbeans.modules.profiler.heapwalk</package>
+                <package>org.netbeans.modules.profiler.heapwalk.details.api</package>
                 <package>org.netbeans.modules.profiler.heapwalk.details.spi</package>
                 <package>org.netbeans.modules.profiler.heapwalk.ui.icons</package>
             </friend-packages>
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/HeapWalker.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/HeapWalker.java
index 5536659c1..62214c689 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/HeapWalker.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/HeapWalker.java
@@ -24,7 +24,6 @@
 import javax.swing.event.ChangeListener;
 import org.netbeans.api.progress.ProgressHandle;
 import org.netbeans.lib.profiler.heap.*;
-import org.netbeans.modules.profiler.heapwalk.ui.HeapWalkerUI;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 import org.openide.util.NbBundle;
@@ -32,9 +31,11 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.List;
 import javax.swing.SwingUtilities;
 import org.netbeans.modules.profiler.ResultsManager;
 import org.netbeans.modules.profiler.api.ProfilerStorage;
+import org.netbeans.modules.profiler.heapwalk.ui.HeapWalkerUI;
 import org.openide.util.Lookup;
 
 
@@ -51,7 +52,8 @@
 
     private File heapDumpFile;
     private HeapFragmentWalker mainHeapWalker;
-    private HeapWalkerUI heapWalkerUI;
+//    private HeapWalkerUI heapWalkerUI;
+    private TopComponent heapWalkerUI;
     private Lookup.Provider heapDumpProject;
     private String heapWalkerName;
 
@@ -61,6 +63,8 @@
     public HeapWalker(Heap heap) {
         heapWalkerName = Bundle.ClassesListController_HeapWalkerDefaultName();
         createMainFragment(heap);
+        
+//        computeRetainedSizes();
     }
 
     public HeapWalker(File heapFile) throws FileNotFoundException, IOException {
@@ -134,6 +138,16 @@ public void run() {
                 }
             });
     }
+    
+    private void computeRetainedSizes() {
+        List<JavaClass> classes = mainHeapWalker.getHeapFragment().getAllClasses();
+        if (classes.size() > 0) {
+            ProgressHandle pd = ProgressHandle.createHandle(Bundle.HeapFragmentWalker_ComputingRetainedMsg());
+            pd.start();
+            classes.get(0).getRetainedSizeByClass();
+            pd.finish();
+        }
+    }
 
     // --- Private implementation ------------------------------------------------
     private static Lookup.Provider computeHeapDumpProject(File heapDumpFile) {
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/BasicExportAction.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/api/ExportAction.java
similarity index 77%
rename from profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/BasicExportAction.java
rename to profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/api/ExportAction.java
index ba7860213..3b6fc1220 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/BasicExportAction.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/api/ExportAction.java
@@ -16,7 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.profiler.heapwalk.details.basic;
+
+package org.netbeans.modules.profiler.heapwalk.details.api;
 
 import java.awt.event.ActionEvent;
 import java.io.File;
@@ -46,26 +47,26 @@
 
 
 @NbBundle.Messages({
-    "BasicExportAction_BasicExportActionName=Export to...",
-    "BasicExportAction_BasicExportActionDescr=Export to...",
-    "BasicExportAction_ExportDialogTitle=Select File or Directory",
-    "BasicExportAction_ExportDialogButton=Export",
-    "BasicExportAction_OverwriteFileCaption=Overwrite Existing File",
-    "BasicExportAction_OverwriteFileMsg=<html><b>File {0} already exists.</b><br><br>Do you want to replace it?</html>",
-    "BasicExportAction_ExportDialogCSVFilter=CSV File (*.csv)",
-    "BasicExportAction_ExportDialogTXTFilter=Text File (*.txt)",
-    "BasicExportAction_ExportDialogBINFilter=Binary File (*.bin)",
-    "BasicExportAction_ExportingViewMsg=Exporting...",
-    "BasicExportAction_NoViewMsg=No view to export.",
-    "BasicExportAction_OomeExportingMsg=<html><b>Not enough memory to save the file.</b><br><br>To avoid this error increase the -Xmx<br>value in the etc/netbeans.conf file in NetBeans IDE installation.</html>",
-    "BasicExportAction_IOException_Exporting_Msg=<html>IOException occurred during export, see IDE log for details</html>",
-    "BasicExportAction_CannotWriteFileMsg=Failed to export File. Reason: {0}."})
-final class BasicExportAction extends AbstractAction {
-    private static final Logger LOGGER = Logger.getLogger(BasicExportAction.class.getName());
+    "ExportAction_BasicExportActionName=Export to...",
+    "ExportAction_BasicExportActionDescr=Export to...",
+    "ExportAction_ExportDialogTitle=Select File or Directory",
+    "ExportAction_ExportDialogButton=Export",
+    "ExportAction_OverwriteFileCaption=Overwrite Existing File",
+    "ExportAction_OverwriteFileMsg=<html><b>File {0} already exists.</b><br><br>Do you want to replace it?</html>",
+    "ExportAction_ExportDialogCSVFilter=CSV File (*.csv)",
+    "ExportAction_ExportDialogTXTFilter=Text File (*.txt)",
+    "ExportAction_ExportDialogBINFilter=Binary File (*.bin)",
+    "ExportAction_ExportingViewMsg=Exporting...",
+    "ExportAction_NoViewMsg=No view to export.",
+    "ExportAction_OomeExportingMsg=<html><b>Not enough memory to save the file.</b><br><br>To avoid this error increase the -Xmx<br>value in the etc/netbeans.conf file in NetBeans IDE installation.</html>",
+    "ExportAction_IOException_Exporting_Msg=<html>IOException occurred during export, see IDE log for details</html>",
+    "ExportAction_CannotWriteFileMsg=Failed to export File. Reason: {0}."})
+public final class ExportAction extends AbstractAction {
+    private static final Logger LOGGER = Logger.getLogger(ExportAction.class.getName());
 
 //~ Inner Interfaces ---------------------------------------------------------------------------------------------------------
 
-    static interface ExportProvider {
+    public static interface ExportProvider {
         //~ Methods --------------------------------------------------------------------------------------------------------------
 
         public void exportData(int exportedFileType, ExportDataDumper eDD);
@@ -117,9 +118,9 @@ File getSelectedFile() {
     private static final String FILE_EXTENSION_CSV = "csv"; // NOI18N
     private static final String FILE_EXTENSION_TXT = "txt"; // NOI18N
     private static final String FILE_EXTENSION_BIN = "bin"; // NOI18N
-    static final int MODE_CSV = 1;
-    static final int MODE_TXT = 2;
-    static final int MODE_BIN = 3;
+    public static final int MODE_CSV = 1;
+    public static final int MODE_TXT = 2;
+    public static final int MODE_BIN = 3;
     private static File exportDir;
 
 
@@ -131,9 +132,9 @@ File getSelectedFile() {
 
     //~ Constructors -------------------------------------------------------------------------------------------------------------
 
-    public BasicExportAction(ExportProvider exportProvider) {
-        putValue(Action.NAME, Bundle.BasicExportAction_BasicExportActionName());
-        putValue(Action.SHORT_DESCRIPTION, Bundle.BasicExportAction_BasicExportActionDescr());
+    public ExportAction(ExportProvider exportProvider) {
+        putValue(Action.NAME, Bundle.ExportAction_BasicExportActionName());
+        putValue(Action.SHORT_DESCRIPTION, Bundle.ExportAction_BasicExportActionDescr());
         putValue(Action.SMALL_ICON, ICON);
         putValue("iconBase", Icons.getResource(GeneralIcons.EXPORT)); // NOI18N
         this.exportProvider = exportProvider;
@@ -161,8 +162,8 @@ private JFileChooser getFileChooser() {
             fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
             fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
             fileChooser.setMultiSelectionEnabled(false);
-            fileChooser.setDialogTitle(Bundle.BasicExportAction_ExportDialogTitle());
-            fileChooser.setApproveButtonText(Bundle.BasicExportAction_ExportDialogButton());
+            fileChooser.setDialogTitle(Bundle.ExportAction_ExportDialogTitle());
+            fileChooser.setApproveButtonText(Bundle.ExportAction_ExportDialogButton());
         }
         fileChooser.resetChoosableFileFilters();
         setFilters();
@@ -172,8 +173,8 @@ private JFileChooser getFileChooser() {
     private boolean checkFileExists(File target) {
         if (target.exists()) {
             if (!ProfilerDialogs.displayConfirmation(
-                    Bundle.BasicExportAction_OverwriteFileMsg(target.getName()),
-                    Bundle.BasicExportAction_OverwriteFileCaption())) {  // choose whether to overwrite
+                    Bundle.ExportAction_OverwriteFileMsg(target.getName()),
+                    Bundle.ExportAction_OverwriteFileCaption())) {  // choose whether to overwrite
                   return false; // user chose not to overwrite
               }
           }
@@ -200,13 +201,13 @@ private SelectedFile selectExportTargetFile(final ExportProvider exportProvider)
         String targetExt = null;
         FileFilter selectedFileFilter = chooser.getFileFilter();
         if (selectedFileFilter==null  // workaround for #227659
-                ||  selectedFileFilter.getDescription().equals(Bundle.BasicExportAction_ExportDialogCSVFilter())) {
+                ||  selectedFileFilter.getDescription().equals(Bundle.ExportAction_ExportDialogCSVFilter())) {
             targetExt=FILE_EXTENSION_CSV;
             exportedFileType=MODE_CSV;
-        } else if (selectedFileFilter.getDescription().equals(Bundle.BasicExportAction_ExportDialogTXTFilter())) {
+        } else if (selectedFileFilter.getDescription().equals(Bundle.ExportAction_ExportDialogTXTFilter())) {
             targetExt=FILE_EXTENSION_TXT;
             exportedFileType=MODE_TXT;
-        } else if (selectedFileFilter.getDescription().equals(Bundle.BasicExportAction_ExportDialogBINFilter())) {
+        } else if (selectedFileFilter.getDescription().equals(Bundle.ExportAction_ExportDialogBINFilter())) {
             targetExt=FILE_EXTENSION_BIN;
             exportedFileType=MODE_BIN;
         }
@@ -230,13 +231,13 @@ private SelectedFile selectExportTargetFile(final ExportProvider exportProvider)
 
         // 3. set type of exported file and return a newly created FileObject
 
-        return new BasicExportAction.SelectedFile(targetDir, targetName, targetExt);
+        return new ExportAction.SelectedFile(targetDir, targetName, targetExt);
     }
 
     @Override
     public void actionPerformed(ActionEvent evt) {
         if (!exportProvider.hasRawData() && !exportProvider.hasText()) { // nothing to export
-            ProfilerDialogs.displayError(Bundle.BasicExportAction_NoViewMsg());
+            ProfilerDialogs.displayError(Bundle.ExportAction_NoViewMsg());
             return;
         }
 
@@ -248,7 +249,7 @@ public void actionPerformed(ActionEvent evt) {
         if (!checkFileExists(file)) return; // user doesn't want to overwrite existing file or it can't be overwritten
 
         new NBSwingWorker(true) {
-            final private ProgressHandle ph = ProgressHandle.createHandle(Bundle.BasicExportAction_ExportingViewMsg());
+            final private ProgressHandle ph = ProgressHandle.createHandle(Bundle.ExportAction_ExportingViewMsg());
             @Override
             protected void doInBackground() {
                 ph.setInitialDelay(500);
@@ -260,12 +261,12 @@ protected void doInBackground() {
                     ExportDataDumper eDD = new ExportDataDumper(fo);
                     exportProvider.exportData(exportedFileType, eDD);
                     if (eDD.getCaughtException()!=null) {
-                        ProfilerDialogs.displayError(eDD.getNumExceptions()+Bundle.BasicExportAction_IOException_Exporting_Msg());
+                        ProfilerDialogs.displayError(eDD.getNumExceptions()+Bundle.ExportAction_IOException_Exporting_Msg());
                     }
                 } catch (OutOfMemoryError e) {
-                    ProfilerDialogs.displayError(Bundle.BasicExportAction_OomeExportingMsg()+e.getMessage());
+                    ProfilerDialogs.displayError(Bundle.ExportAction_OomeExportingMsg()+e.getMessage());
                 } catch (IOException e1) {
-                    ProfilerDialogs.displayError(Bundle.BasicExportAction_CannotWriteFileMsg(e1.getLocalizedMessage()));
+                    ProfilerDialogs.displayError(Bundle.ExportAction_CannotWriteFileMsg(e1.getLocalizedMessage()));
                     LOGGER.log(Level.WARNING, e1.toString());
                 }
             }
@@ -293,11 +294,11 @@ public boolean accept(File f) {
         @Override
         public String getDescription() {
             if (FILE_EXTENSION_CSV.equals(extension)) {
-                return Bundle.BasicExportAction_ExportDialogCSVFilter();
+                return Bundle.ExportAction_ExportDialogCSVFilter();
             } else if (FILE_EXTENSION_TXT.equals(extension)) {
-                return Bundle.BasicExportAction_ExportDialogTXTFilter();
+                return Bundle.ExportAction_ExportDialogTXTFilter();
             } else if (FILE_EXTENSION_BIN.equals(extension)) {
-                return Bundle.BasicExportAction_ExportDialogBINFilter();
+                return Bundle.ExportAction_ExportDialogBINFilter();
             } else {
                 return null;
             }
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/StringDecoder.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/api/StringDecoder.java
similarity index 91%
rename from profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/StringDecoder.java
rename to profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/api/StringDecoder.java
index eff27ede5..ab6518f30 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/StringDecoder.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/api/StringDecoder.java
@@ -16,7 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.profiler.heapwalk.details.basic;
+
+package org.netbeans.modules.profiler.heapwalk.details.api;
 
 import java.util.List;
 import org.netbeans.lib.profiler.heap.Heap;
@@ -26,14 +27,14 @@
  *
  * @author Tomas Hurka
  */
-class StringDecoder {
+public final class StringDecoder {
     
     private final byte coder;
     private final List<String> values;
     private int HI_BYTE_SHIFT;
     private int LO_BYTE_SHIFT;
 
-    StringDecoder(Heap heap, byte c, List<String> val) {
+    public StringDecoder(Heap heap, byte c, List<String> val) {
         coder = c;
         values = val;
         if (coder == 1) {
@@ -43,7 +44,7 @@
         }
     }
 
-    int getStringLength() {
+    public int getStringLength() {
         int size = values.size();
         switch (coder) {
             case -1:
@@ -57,7 +58,7 @@ int getStringLength() {
         }
     }
 
-    String getValueAt(int index) {
+    public String getValueAt(int index) {
         switch (coder) {
             case -1:
                 return values.get(index);
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/ArrayValueView.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/ArrayValueView.java
index d1e72ab02..94bfe18fb 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/ArrayValueView.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/ArrayValueView.java
@@ -18,6 +18,8 @@
  */
 package org.netbeans.modules.profiler.heapwalk.details.basic;
 
+import org.netbeans.modules.profiler.heapwalk.details.api.ExportAction;
+import org.netbeans.modules.profiler.heapwalk.details.api.StringDecoder;
 import java.awt.BorderLayout;
 import java.awt.Container;
 import java.awt.Cursor;
@@ -58,11 +60,11 @@
     "ArrayValueView_Truncated=... <truncated>",                                     // NOI18N
     "ArrayValueView_Value=Value:",                                                  // NOI18N
     "ArrayValueView_Items=Array items:",                                            // NOI18N
-    "ArrayValueView_All=Show all",                                                  // NOI18N
-    "ArrayValueView_Save=Save to file",                                             // NOI18N
+    "ArrayValueView_All=Show All",                                                  // NOI18N
+    "ArrayValueView_Save=Save to File",                                             // NOI18N
     "ArrayValueView_OutOfMemory=Out of memory - value too long."                    // NOI18N
 })
-final class ArrayValueView extends DetailsProvider.View implements Scrollable, BasicExportAction.ExportProvider {
+final class ArrayValueView extends DetailsProvider.View implements Scrollable, ExportAction.ExportProvider {
     
     private static final int MAX_PREVIEW_LENGTH = 256;
     private static final int MAX_ARRAY_ITEMS = 1000;
@@ -144,6 +146,7 @@ public void run() {
                 view.setLineWrap(true);
                 view.setWrapStyleWord(true);
                 view.setText(preview);
+                try { view.setCaretPosition(0); } catch (IllegalArgumentException e) {}
                 
                 JScrollPane viewScroll = new JScrollPane(view,
                         JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
@@ -163,7 +166,7 @@ public void run() {
                 
                 JButton save = htmlButton(Bundle.ArrayValueView_Save(), !preview.isEmpty(), new Runnable() {
                     public void run() {
-                        new BasicExportAction(ArrayValueView.this).actionPerformed(null);
+                        new ExportAction(ArrayValueView.this).actionPerformed(null);
                     }
                 });
                 c = new GridBagConstraints();
@@ -206,6 +209,7 @@ public void run() {
                     public void run() {
                         try {
                             view.setText(preview);
+                            try { view.setCaretPosition(0); } catch (IllegalArgumentException e) {}
                             view.setEnabled(true);
                         } catch (OutOfMemoryError e) {
                             ProfilerDialogs.displayError(Bundle.ArrayValueView_OutOfMemory());
@@ -298,14 +302,14 @@ public void exportData(int exportedFileType, ExportDataDumper eDD) {
                 String value = decoder.getValueAt(i);
                 
                 switch (exportedFileType) {
-                    case BasicExportAction.MODE_CSV:
+                    case ExportAction.MODE_CSV:
                         eDD.dumpData(value);
                         eDD.dumpData(comma);
                         break;
-                    case BasicExportAction.MODE_TXT:
+                    case ExportAction.MODE_TXT:
                         eDD.dumpData(value);
                         break;
-                    case BasicExportAction.MODE_BIN:
+                    case ExportAction.MODE_BIN:
                         byte b = Byte.valueOf(value);
                         eDD.dumpByte(b);
                         break;
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/StringDetailsProvider.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/StringDetailsProvider.java
index 8045bc236..69f19a084 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/StringDetailsProvider.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/basic/StringDetailsProvider.java
@@ -18,6 +18,7 @@
  */
 package org.netbeans.modules.profiler.heapwalk.details.basic;
 
+import org.netbeans.modules.profiler.heapwalk.details.api.StringDecoder;
 import java.util.List;
 import org.netbeans.lib.profiler.heap.Heap;
 import org.netbeans.lib.profiler.heap.Instance;
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/jdk/ui/ComponentDetailsProvider.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/jdk/ui/ComponentDetailsProvider.java
index 8a66adf6f..ed60678fe 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/jdk/ui/ComponentDetailsProvider.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/jdk/ui/ComponentDetailsProvider.java
@@ -51,7 +51,8 @@
  * @author Jiri Sedlacek
  */
 @NbBundle.Messages({
-    "ComponentDetailsProvider_NewWindow=Analyze in new window"
+    "ComponentDetailsProvider_NewWindow=Analyze in new window",
+    "ComponentDetailsProvider_InvisibleComponentPrefix=[invisible]"
 })
 @ServiceProvider(service=DetailsProvider.class)
 public final class ComponentDetailsProvider extends DetailsProvider.Basic {
@@ -77,38 +78,49 @@ public ComponentDetailsProvider() {
     }
     
     public String getDetailsString(String className, Instance instance, Heap heap) {
+        String string = null;
+        
         if (JLABEL_MASK.equals(className) ||                                        // JLabel+
             ABSTRACTBUTTON_MASK.equals(className)) {                                // AbstractButton+
-            return DetailsUtils.getInstanceFieldString(
+            string = DetailsUtils.getInstanceFieldString(
                     instance, "text", heap);                                        // NOI18N
         } else if (JTOOLTIP_MASK.equals(className)) {                               // JToolTip+
-            return DetailsUtils.getInstanceFieldString(
+            string = DetailsUtils.getInstanceFieldString(
                     instance, "tipText", heap);                                     // NOI18N
         } else if (JFILECHOOSER_MASK.equals(className)) {                           // JFileChooser+
-            return DetailsUtils.getInstanceFieldString(
+            string = DetailsUtils.getInstanceFieldString(
                     instance, "dialogTitle", heap);                                 // NOI18N
         } else if (JINTERNALFRAME_MASK.equals(className) ||                         // JInternalFrame+
                    FRAME_MASK.equals(className) ||                                  // Frame+
                    DIALOG_MASK.equals(className)) {                                 // Dialog+
-            return DetailsUtils.getInstanceFieldString(
+            string = DetailsUtils.getInstanceFieldString(
                     instance, "title", heap);                                       // NOI18N
         } else if (TABLECOLUMN_MASK.equals(className)) {                            // TableColumn+
-            return DetailsUtils.getInstanceFieldString(
+            string = DetailsUtils.getInstanceFieldString(
                     instance, "headerValue", heap);                                 // NOI18N
         } else if (JPROGRESSBAR_MASK.equals(className)) {                           // JProgressBar+
             boolean b = DetailsUtils.getBooleanFieldValue(
                     instance, "paintString", false);                                // NOI18N
-            if (b) return DetailsUtils.getInstanceFieldString(
+            if (b) string = DetailsUtils.getInstanceFieldString(
                     instance, "progressString", heap);                              // NOI18N
         }
         
-        // Value for a generic Component
-        String string = getStringField(instance, "displayName", heap);
-        if (string == null) string = getStringField(instance, "label", heap);
-        if (string == null) string = getStringField(instance, "name", heap);
-        // TODO: check tooltip
+        if (string == null) {
+            // Value for a generic Component
+            string = getStringField(instance, "displayName", heap);
+            if (string == null) string = getStringField(instance, "label", heap);
+            if (string == null) string = getStringField(instance, "name", heap);
+            // TODO: check tooltip
+
+            if (string != null && string.trim().isEmpty()) string = null;
+        }
         
-        if (string != null && string.trim().isEmpty()) string = null;
+        if (string != null) {
+            // Mark invisible components
+            boolean b = DetailsUtils.getBooleanFieldValue(
+                    instance, "visible", false);                                    // NOI18N
+            if (!b) string = Bundle.ComponentDetailsProvider_InvisibleComponentPrefix() + " " + string; // NOI18N
+        }
         
         return string;
     }
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/netbeans/PlatformDetailsProvider.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/netbeans/PlatformDetailsProvider.java
index 6df7c60c9..662529533 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/netbeans/PlatformDetailsProvider.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/netbeans/PlatformDetailsProvider.java
@@ -32,12 +32,14 @@
 /**
  *
  * @author Tomas Hurka
+ * @author Jiri Sedlacek
  */
 @ServiceProvider(service=DetailsProvider.class)
 public class PlatformDetailsProvider extends DetailsProvider.Basic {
 
     private static final String STANDARD_MODULE = "org.netbeans.Module+"; // NOI18N
     private static final String MODULE_DATA = "org.netbeans.ModuleData+"; // NOI18N
+    private static final String DEPENDENCY = "org.openide.modules.Dependency"; // NOI18N
     private static final String SPECIFICATION_VERSION = "org.openide.modules.SpecificationVersion"; // NOI18N
     private static final String ABSTRACT_NODE = "org.openide.nodes.AbstractNode+"; // NOI18N
     private static final String MULTI_FILE_ENTRY = "org.openide.loaders.MultiDataObject$Entry+"; // NOI18N
@@ -68,7 +70,7 @@ protected boolean removeEldestEntry(Map.Entry<Long, String> eldest) {
     };
     
     public PlatformDetailsProvider() {
-        super(STANDARD_MODULE,MODULE_DATA,SPECIFICATION_VERSION,
+        super(STANDARD_MODULE,MODULE_DATA,DEPENDENCY,SPECIFICATION_VERSION,
               ABSTRACT_NODE,MULTI_FILE_ENTRY,DATA_OBJECT,JAR_FILESYSTEM,
               FILE_OBJ,FOLDER_OBJ, FILE_NAME,FOLDER_NAME,ABSTRACT_FOLDER,
               BFS_BASE,
@@ -96,6 +98,12 @@ private String getDetailsStringImpl(String className, Instance instance, Heap he
                 return codeName;
             }
             return DetailsUtils.getInstanceFieldString(instance, "data", heap);     // NOI18N
+        } else if (DEPENDENCY.equals(className)) {
+            String name = DetailsUtils.getInstanceFieldString(instance, "name", heap);     // NOI18N
+            String version = DetailsUtils.getInstanceFieldString(instance, "version", heap);     // NOI18N
+            int type = DetailsUtils.getIntFieldValue(instance, "type", -1); // NOI18N
+            int comparison = DetailsUtils.getIntFieldValue(instance, "comparison", -1); // NOI18N
+            return DependencyResolver.toString(name, version, type, comparison);
         } else if (SPECIFICATION_VERSION.equals(className)) {
             PrimitiveArrayInstance digits = (PrimitiveArrayInstance) instance.getValueOfField("digits"); // NOI18N
             if (digits != null) {
@@ -1585,6 +1593,50 @@ public CharSequences() {
      */
     private interface CompactCharSequence extends CharSequence {
     }
+    
+    
+    private static class DependencyResolver {
+        
+        private final static int TYPE_MODULE = 1;
+        private final static int TYPE_PACKAGE = 2;
+        private final static int TYPE_JAVA = 3;
+        private final static int TYPE_IDE = 4;
+        private final static int TYPE_REQUIRES = 5;
+        private final static int TYPE_NEEDS = 6;
+        private final static int TYPE_RECOMMENDS = 7;
+        private final static int COMPARE_SPEC = 1;
+        private final static int COMPARE_IMPL = 2;
+        private final static int COMPARE_ANY = 3;
+        
+        static String toString(String name, String version, int type, int comparison) {
+            StringBuilder buf = new StringBuilder(100);
+
+            if (type == TYPE_MODULE) {
+                buf.append("module "); // NOI18N
+            } else if (type == TYPE_PACKAGE) {
+                buf.append("package "); // NOI18N
+            } else if (type == TYPE_REQUIRES) {
+                buf.append("requires "); // NOI18N
+            } else if (type == TYPE_NEEDS) {
+                buf.append("needs "); // NOI18N
+            } else if (type == TYPE_RECOMMENDS) {
+                buf.append("recommends "); // NOI18N
+            }
+
+            buf.append(name);
+
+            if (comparison == COMPARE_IMPL) {
+                buf.append(" = "); // NOI18N
+                buf.append(version);
+            } else if (comparison == COMPARE_SPEC) {
+                buf.append(" > "); // NOI18N
+                buf.append(version);
+            }
+
+            return buf.toString();
+        }
+        
+    }
 
     //</editor-fold>
 
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/spi/DetailsProvider.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/spi/DetailsProvider.java
index 41b7cfadb..4b153dabc 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/spi/DetailsProvider.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/details/spi/DetailsProvider.java
@@ -20,10 +20,12 @@
 
 import java.awt.BorderLayout;
 import java.awt.Component;
+import javax.swing.JComponent;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import org.netbeans.lib.profiler.heap.Heap;
 import org.netbeans.lib.profiler.heap.Instance;
+import org.netbeans.lib.profiler.ui.UIUtils;
 import org.netbeans.modules.profiler.heapwalk.model.BrowserUtils;
 import org.openide.util.NbBundle;
 import org.openide.util.RequestProcessor;
@@ -33,7 +35,7 @@
  * @author Jiri Sedlacek
  */
 @NbBundle.Messages({
-    "BrowserUtils_Loading=Loading content..."                                   // NOI18N
+    "BrowserUtils_Loading=<loading content...>"                                   // NOI18N
 })
 public abstract class DetailsProvider {
     
@@ -80,11 +82,22 @@ protected Basic(String... supportedClasses) {
         private Instance instance;
         private Heap heap;
         
-        // [Event Dispatch Thread] Constructor for default initial UI ("Loading content...")
+        // [Event Dispatch Thread] Constructor for default initial UI ("<loading content...>")
         protected View(Instance instance, Heap heap) {
-            this(instance, heap, new JLabel(Bundle.BrowserUtils_Loading(), JLabel.CENTER) {
-                public void addNotify() { setEnabled(false); }
-            });
+            this(instance, heap, initialView());
+        }
+        
+        private static JComponent initialView() {
+            JLabel loading = new JLabel(Bundle.BrowserUtils_Loading(), JLabel.CENTER);
+            loading.setEnabled(false);
+            
+            JPanel loadingContainer = new JPanel(new BorderLayout());
+            loadingContainer.setOpaque(true);
+            loadingContainer.setBackground(UIUtils.getProfilerResultsBackground());
+            loadingContainer.setEnabled(false);
+            loadingContainer.add(loading, BorderLayout.CENTER);
+            
+            return loadingContainer;
         }
         
         // [Event Dispatch Thread] Constructor for custom initial UI
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/model/AbstractHeapWalkerNode.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/model/AbstractHeapWalkerNode.java
index 31495c110..0bb12cae4 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/model/AbstractHeapWalkerNode.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/model/AbstractHeapWalkerNode.java
@@ -126,6 +126,10 @@ public String getName() {
 
         return name;
     }
+    
+    public void setParent(HeapWalkerNode parent) {
+        this.parent = parent;
+    }
 
     public HeapWalkerNode getParent() {
         return parent;
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/model/HeapWalkerNode.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/model/HeapWalkerNode.java
index dbfbcf96d..0919c9f3e 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/model/HeapWalkerNode.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/model/HeapWalkerNode.java
@@ -19,8 +19,12 @@
 
 package org.netbeans.modules.profiler.heapwalk.model;
 
+import java.util.ArrayList;
+import java.util.List;
 import org.netbeans.lib.profiler.results.CCTNode;
 import javax.swing.Icon;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
 
 
 /**
@@ -79,4 +83,19 @@
      * There are two different algorithms for generating childs in both Browsers.
      */
     public abstract int getMode();
+    
+    
+    public static TreePath fromNode(TreeNode node) {
+        return fromNode(node, null);
+    }
+    
+    public static TreePath fromNode(TreeNode node, TreeNode root) {
+        List l = new ArrayList();
+        while (node != root) {
+            l.add(0, node);
+            node = node.getParent();
+        }
+        if (node != null) l.add(0, node);
+        return new TreePath(l.toArray(new Object[0]));
+    }
 }
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/oql/ui/OQLEditor.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/oql/ui/OQLEditor.java
index dcec57af9..375641fce 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/oql/ui/OQLEditor.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/oql/ui/OQLEditor.java
@@ -24,7 +24,6 @@
 import java.awt.Rectangle;
 import javax.swing.JEditorPane;
 import javax.swing.JPanel;
-import javax.swing.JTextArea;
 import javax.swing.UIManager;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
@@ -33,12 +32,15 @@
 import javax.swing.text.BadLocationException;
 import javax.swing.text.Caret;
 import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
 import org.netbeans.lib.profiler.ui.UIUtils;
 import org.netbeans.lib.profiler.ui.components.NoCaret;
 import org.netbeans.modules.profiler.oql.engine.api.OQLEngine;
 import org.netbeans.modules.profiler.oql.engine.api.OQLException;
 import org.netbeans.modules.profiler.oql.spi.OQLEditorImpl;
 import org.openide.awt.StatusDisplayer;
+import org.openide.util.Exceptions;
 import org.openide.util.Lookup;
 
 /**
@@ -63,6 +65,8 @@
 
     private Color lastBgColor = null;
     private Caret lastCaret = null;
+    
+    private Font font;
 
     public OQLEditor(OQLEngine engine) {
         this.engine = engine;
@@ -90,11 +94,20 @@ public void callback(boolean lexingResult) {
             queryEditor.getCaret().addChangeListener(new ChangeListener() {
                 public void stateChanged(ChangeEvent e) {
                     try {
-                        Rectangle edit = queryEditor.getUI().modelToView(queryEditor, queryEditor.getCaretPosition());
+                        Rectangle edit = queryEditor == null ? null :
+                                         queryEditor.getUI().modelToView(
+                                         queryEditor, queryEditor.getCaretPosition());
                         if (edit != null) queryEditor.scrollRectToVisible(edit);
-                    } catch (BadLocationException ex) {}
+                    } catch (BadLocationException ex) {
+                        Exceptions.printStackTrace(ex);
+                    }
                 }
             });
+            
+            Element root = queryEditor.getDocument().getDefaultRootElement();
+            String family = StyleConstants.getFontFamily(root.getAttributes());
+            int size = StyleConstants.getFontSize(root.getAttributes());
+            font = new Font(family, Font.PLAIN, size);
         } else {
             queryEditor = new JEditorPane() {
                 public void setText(String text) {
@@ -112,8 +125,8 @@ public void setText(String text) {
                 queryEditor.setContentType("text/x-oql"); // NOI18N
             } catch (NullPointerException e) {}
             
-            int fontsize = new JTextArea().getFont().getSize();
-            queryEditor.setFont(new Font("Monospaced", Font.PLAIN, fontsize)); // NOI18N
+            font = Font.decode("Monospaced"); // NOI18N
+            queryEditor.setFont(font);
             lexervalid = true; // no lexer info available; assume the lexing info is valid
         }
 
@@ -128,11 +141,11 @@ public void setText(String text) {
     }
 
     public void setScript(String script) {
-        editor().setText(script);
+        getEditor().setText(script);
     }
 
     public String getScript() {
-        return editor().getText();
+        return getEditor().getText();
     }
 
     @Override
@@ -149,7 +162,7 @@ public void setOpaque(boolean isOpaque) {
 
     @Override
     public void requestFocus() {
-        editor().requestFocus();
+        getEditor().requestFocus();
     }
 
     final private void validateScript() {
@@ -169,7 +182,7 @@ final private void validateScript() {
     }
 
     public void setEditable(boolean b) {
-        JEditorPane editor = editor();
+        JEditorPane editor = getEditor();
         if (editor.isEditable() == b) return;
         
         editor.setEditable(b);
@@ -186,10 +199,15 @@ public void setEditable(boolean b) {
     }
 
     public boolean isEditable() {
-        return editor().isEditable();
+        return getEditor().isEditable();
+    }
+    
+    public Font getFont() {
+        if (queryEditor == null) init();
+        return font;
     }
     
-    private JEditorPane editor() {
+    public JEditorPane getEditor() {
         if (queryEditor == null) init();
         return queryEditor;
     }
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/HeapWalkerIcons.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/HeapWalkerIcons.java
index 72bf640f3..6c42befd7 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/HeapWalkerIcons.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/HeapWalkerIcons.java
@@ -42,5 +42,6 @@
     public static final String SYSTEM_INFO = "HeapWalkerIcons.SystemInfo"; // NOI18N
     public static final String WINDOW = "HeapWalkerIcons.Window"; // NOI18N
     public static final String BIGGEST_OBJECTS = "HeapWalkerIcons.BiggestObjects"; // NOI18N
+    public static final String OQL_CONSOLE = "HeapWalkerIcons.OQLConsole"; // NOI18N
     
 }
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/impl/HeapWalkerIconsProviderImpl.java b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/impl/HeapWalkerIconsProviderImpl.java
index d80dc321b..ac682b30e 100644
--- a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/impl/HeapWalkerIconsProviderImpl.java
+++ b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/impl/HeapWalkerIconsProviderImpl.java
@@ -47,6 +47,7 @@ protected final void initStaticImages(Map<String, String> cache) {
         cache.put(HeapWalkerIcons.SYSTEM_INFO, "sysinfo.png"); // NOI18N
         cache.put(HeapWalkerIcons.WINDOW, "window.png"); // NOI18N
         cache.put(HeapWalkerIcons.BIGGEST_OBJECTS, "biggestObjects.png"); // NOI18N
+        cache.put(HeapWalkerIcons.OQL_CONSOLE, "oqlConsole.png"); // NOI18N
     }
     
 }
diff --git a/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/impl/oqlConsole.png b/profiler.heapwalker/src/org/netbeans/modules/profiler/heapwalk/ui/icons/impl/oqlConsole.png
new file mode 100644
index 000000000..e69de29bb
diff --git a/profiler.oql/manifest.mf b/profiler.oql/manifest.mf
index f220c9e8e..fbb203d15 100644
--- a/profiler.oql/manifest.mf
+++ b/profiler.oql/manifest.mf
@@ -3,5 +3,5 @@ AutoUpdate-Show-In-Client: false
 OpenIDE-Module: org.netbeans.modules.profiler.oql/2
 OpenIDE-Module-Layer: org/netbeans/modules/profiler/oql/layer.xml
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/profiler/oql/Bundle.properties
-OpenIDE-Module-Specification-Version: 2.17
+OpenIDE-Module-Specification-Version: 2.19
 
diff --git a/profiler.oql/nbproject/project.xml b/profiler.oql/nbproject/project.xml
index 9493b2d3a..904b859e8 100644
--- a/profiler.oql/nbproject/project.xml
+++ b/profiler.oql/nbproject/project.xml
@@ -100,6 +100,7 @@
                 </test-type>
             </test-dependencies>
             <friend-packages>
+                <friend>com.sun.tools.visualvm.heapviewer</friend>
                 <friend>com.sun.tools.visualvm.modules.oqlsyntax</friend>
                 <friend>oracle.jdevimpl.profiler.impl</friend>
                 <friend>org.netbeans.modules.profiler.heapwalker</friend>
diff --git a/profiler.oql/src/org/netbeans/modules/profiler/oql/engine/api/impl/OQLEngineImpl.java b/profiler.oql/src/org/netbeans/modules/profiler/oql/engine/api/impl/OQLEngineImpl.java
index 9d6a80c6d..d359a4dd1 100644
--- a/profiler.oql/src/org/netbeans/modules/profiler/oql/engine/api/impl/OQLEngineImpl.java
+++ b/profiler.oql/src/org/netbeans/modules/profiler/oql/engine/api/impl/OQLEngineImpl.java
@@ -356,13 +356,18 @@ public Object unwrapJavaObject(Object object, boolean tryAssociativeArray) {
         if (object == null) return null;
         String className = object.getClass().getName();
         boolean isNativeJS = className.contains(".javascript.")     // NOI18N
-                          || className.equals("jdk.nashorn.api.scripting.ScriptObjectMirror"); // NOI18N
+                          || className.equals("jdk.nashorn.api.scripting.ScriptObjectMirror")  // NOI18N
+                          || className.startsWith("com.oracle.truffle.object.") // NOI18N
+                          || className.equals("org.graalvm.polyglot.Value"); // NOI18N
 
         try {
-            Object ret = ((Invocable)engine).invokeFunction("unwrapJavaObject", object); // NOI18N
-            if (isNativeJS && (ret == null || ret.equals(object)) && tryAssociativeArray) {
+            Object ret = ((Invocable)engine).invokeFunction("unwrapJavaObjectRes", object); // NOI18N
+            if (isNativeJS && ret == null && tryAssociativeArray) {
                 ret = ((Invocable)engine).invokeFunction("unwrapMap", object); // NOI18N
             }
+            if (ret == null) {
+                return object;
+            }
             return ret;
         } catch (Exception ex) {
             LOGGER.log(Level.WARNING, "Error unwrapping JS object", ex); // NOI18N
@@ -383,7 +388,7 @@ private void init(Snapshot snapshot) throws RuntimeException {
             engine.put("heap", heap); // NOI18N
             engine.put("cancelled", cancelled); // NOI18N
         } catch (Exception ex) {
-            LOGGER.log(Level.SEVERE, "Error initializing snapshot", ex); // NOI18N
+            LOGGER.log(Level.INFO, "Error initializing snapshot", ex); // NOI18N
             throw new RuntimeException(ex);
         }
     }
diff --git a/profiler.oql/src/org/netbeans/modules/profiler/oql/engine/api/impl/hat.js b/profiler.oql/src/org/netbeans/modules/profiler/oql/engine/api/impl/hat.js
index 76971c4db..f756ce7b6 100644
--- a/profiler.oql/src/org/netbeans/modules/profiler/oql/engine/api/impl/hat.js
+++ b/profiler.oql/src/org/netbeans/modules/profiler/oql/engine/api/impl/hat.js
@@ -17,7 +17,17 @@
  * under the License.
  */
 
-var hatPkg = Packages.org.netbeans.modules.profiler.oql.engine.api.impl;
+var ReachableExcludes = Java.type("org.netbeans.modules.profiler.oql.engine.api.impl.ReachableExcludes");
+var ReachableObjects = Java.type("org.netbeans.modules.profiler.oql.engine.api.impl.ReachableObjects");
+
+var JavaClass = Java.type("org.netbeans.lib.profiler.heap.JavaClass");
+var Instance = Java.type("org.netbeans.lib.profiler.heap.Instance");
+var ObjectArrayInstance = Java.type("org.netbeans.lib.profiler.heap.ObjectArrayInstance");
+var PrimitiveArrayInstance = Java.type("org.netbeans.lib.profiler.heap.PrimitiveArrayInstance");
+var Field = Java.type("org.netbeans.lib.profiler.heap.Field");
+var FieldValue = Java.type("org.netbeans.lib.profiler.heap.FieldValue");
+var GCRoot = Java.type("org.netbeans.lib.profiler.heap.GCRoot");
+
 var snapshot;
 
 /**
@@ -248,7 +258,7 @@ function wrapJavaValue(thing) {
     //    printStackTrace();
     //    print(thing);
 
-    if (thing instanceof Packages.org.netbeans.lib.profiler.heap.FieldValue) {
+    if (thing instanceof FieldValue) {
         var type = thing.field.type;
 
         // map primitive values to closest JavaScript primitives
@@ -267,9 +277,9 @@ function wrapJavaValue(thing) {
             // wrap Java object as script object
             return wrapJavaObject(thing.instance);
         }
-    } else if (thing instanceof Packages.org.netbeans.lib.profiler.heap.GCRoot) {
+    } else if (thing instanceof GCRoot) {
         return wrapRoot(thing);
-    } else if (thing instanceof Packages.org.netbeans.lib.profiler.heap.Field) {
+    } else if (thing instanceof Field) {
         return wrapField(thing);
     } else {
         return wrapJavaObject(thing);
@@ -278,28 +288,18 @@ function wrapJavaValue(thing) {
 
 // HAT Java model object wrapper. Handles all cases 
 // (instance, object/primitive array and Class objects)	
-function javaObject(jobject) {		
-    //        // FIXME: Do I need this? or can I assume that these would
-    //        // have been resolved already?
-    //        if (jobject instanceof hatPkg.model.JavaObjectRef) {
-    //            jobject = jobject.dereference();
-    //            if (jobject instanceof hatPkg.model.HackJavaValue) {
-    //                print(jobject);
-    //                return null;
-    //            }
-    //        }
-
+function javaObject(jobject) {
     //        print(jobject.getClass());
-    if (jobject instanceof Packages.org.netbeans.lib.profiler.heap.JavaClass) {
+    if (jobject instanceof JavaClass) {
         //            print("wrapping as Class");
         return new JavaClassWrapper(jobject);
-    } else if (jobject instanceof Packages.org.netbeans.lib.profiler.heap.ObjectArrayInstance) {
+    } else if (jobject instanceof ObjectArrayInstance) {
         //            print("wrapping as ObjectArray");
         return new JavaObjectArrayWrapper(jobject);
-    } else if (jobject instanceof Packages.org.netbeans.lib.profiler.heap.PrimitiveArrayInstance) {
+    } else if (jobject instanceof PrimitiveArrayInstance) {
         // print("wrapping as ValueArray");
         return new JavaValueArrayWrapper(jobject);
-    } else if (jobject instanceof Packages.org.netbeans.lib.profiler.heap.Instance) {
+    } else if (jobject instanceof Instance) {
         //            print("wrapping as Instance");
         return new JavaObjectWrapper(jobject);
     } else {
@@ -552,7 +552,7 @@ function unwrapJavaObject(jobject) {
     //    print("Unwrapping object");
     //    print(typeof(jobject));
     
-    if (!(jobject instanceof Packages.org.netbeans.lib.profiler.heap.Instance)) {
+    if (!(jobject instanceof Instance)) {
         if (jobject instanceof Array) {
             //            print("Object is array");
             var arr = new java.util.ArrayList(jobject.length);
@@ -577,7 +577,7 @@ function unwrapJavaObject(jobject) {
             //print("unwrapJavaObject: " + jobject + ", " + e);
             jobject = undefined;
         }
-    } 
+    }
     return jobject;
 }
 
@@ -600,6 +600,15 @@ function unwrapArray(jsobject) {
     return array;
 }
 
+function unwrapJavaObjectRes(jobject) {
+    var ret = unwrapJavaObject(jobject);
+
+    if (ret == jobject) {
+        return null;
+    }
+    return ret;
+}
+
 /**
  * The result object supports the following methods:
  * 
@@ -957,9 +966,9 @@ function identical(o1, o2) {
 function objectid(jobject) {
     try {
         jobject = unwrapJavaObject(jobject);
-        if (jobject instanceof Packages.org.netbeans.lib.profiler.heap.Instance) {
+        if (jobject instanceof Instance) {
             return String(jobject.instanceId);
-        } else if (jobject instanceof Packages.org.netbeans.lib.profiler.heap.JavaClass) {
+        } else if (jobject instanceof JavaClass) {
             return String(jobject.javaClassId);
         }
     } catch (e) {
@@ -1050,7 +1059,7 @@ function reachables(jobject, excludes) {
             excludedFields[excludedFields.length] = st.nextToken().trim();
         }
         if (excludedFields.length > 0) { 
-            excludes = new hatPkg.ReachableExcludes() {
+            excludes = new ReachableExcludes() {
                 isExcluded: function (field) {
                     for (var index in excludedFields) {
                         if (field.equals(excludedFields[index])) {
@@ -1064,12 +1073,12 @@ function reachables(jobject, excludes) {
             // nothing to filter...
             excludes = null;
         }
-    } else if (! (excludes instanceof hatPkg.ReachableExcludes)) {
+    } else if (! (excludes instanceof ReachableExcludes)) {
         excludes = null;
     }
 
     jobject = unwrapJavaObject(jobject);
-    var ro = new hatPkg.ReachableObjects(jobject, excludes);  
+    var ro = new ReachableObjects(jobject, excludes);
     return wrapIterator(ro.reachables, true);
 }
 
@@ -1083,9 +1092,9 @@ function reachables(jobject, excludes) {
 function refers(from, to) {
     try {
         var tmp = unwrapJavaObject(from);
-        if (tmp instanceof Packages.org.netbeans.lib.profiler.heap.JavaClass) {
+        if (tmp instanceof JavaClass) {
             from = from.statics;
-        } else if (tmp instanceof Packages.org.netbeans.lib.profiler.heap.PrimitiveArrayInstance) {
+        } else if (tmp instanceof PrimitiveArrayInstance) {
             return false;
         }
         for (var i in from) {
@@ -1172,18 +1181,19 @@ function toHtml(obj) {
     var tmp = unwrapJavaObject(obj);
     if (tmp != undefined) {
         //print("1");
-        if (tmp instanceof Packages.org.netbeans.lib.profiler.heap.JavaClass) {
+        if (tmp instanceof JavaClass) {
             //print("2");
+            // must use the same format as defined in org.netbeans.modules.profiler.heapwalker.v2.utils.HeapUtils.classToHtml()
             var id = tmp.javaClassId;
             var name = tmp.name;
-            return "<a href='file://class/" + name + "'>class " + name + "</a>";
-        }else if (tmp instanceof Packages.org.netbeans.lib.profiler.heap.Instance) {
+            return "<a href='file://class/" + id + "' name='" + id + "'>class " + name + "</a>";
+        }else if (tmp instanceof Instance) {
             //print("3");
+            // must use the same format as defined in org.netbeans.modules.profiler.heapwalker.v2.utils.HeapUtils.instanceToHtml()
             var id = tmp.instanceId;
             var number = tmp.instanceNumber;
             var name = tmp.javaClass.name;
-            return "<a href='file://instance/" + name +"@" + id + "'>" +
-            name + "#" + number + "</a>";
+            return "<a href='file://instance/" + id + "' name='" + id + "'>" + name + "#" + number + "</a>";
         }
         //print("31 "+typeof(tmp));
     }
diff --git a/profiler.oql/test/unit/src/org/netbeans/modules/profiler/oql/engine/api/impl/OQLEngineTest.java b/profiler.oql/test/unit/src/org/netbeans/modules/profiler/oql/engine/api/impl/OQLEngineTest.java
index 8e000ac69..3748dfa85 100644
--- a/profiler.oql/test/unit/src/org/netbeans/modules/profiler/oql/engine/api/impl/OQLEngineTest.java
+++ b/profiler.oql/test/unit/src/org/netbeans/modules/profiler/oql/engine/api/impl/OQLEngineTest.java
@@ -506,7 +506,7 @@ public boolean visit(Object o) {
     public void testMap() throws Exception {
         System.out.println("map");
 
-        final String[] output = new String[] {"", "$assertionsDisabled=true\nserialVersionUID=301077366599181570\ntmpdir=null\ncounter=-1\ntmpFileLock=<a href='file://instance/java.lang.Object@1684106928'>java.lang.Object#6</a>\npathSeparator=<a href='file://instance/java.lang.String@1684106888'>java.lang.String#101</a>\npathSeparatorChar=:\nseparator=<a href='file://instance/java.lang.String@1684106848'>java.lang.String#100</a>\nseparatorChar=/\nfs=<a href='file://instance/java.io.UnixFileSystem@1684106408'>java.io.UnixFileSystem#1</a>\n<classLoader>=null\n"};
+        final String[] output = new String[] {"", "$assertionsDisabled=true\nserialVersionUID=301077366599181570\ntmpdir=null\ncounter=-1\ntmpFileLock=<a href='file://instance/1684106928' name='1684106928'>java.lang.Object#6</a>\npathSeparator=<a href='file://instance/1684106888' name='1684106888'>java.lang.String#101</a>\npathSeparatorChar=:\nseparator=<a href='file://instance/1684106848' name='1684106848'>java.lang.String#100</a>\nseparatorChar=/\nfs=<a href='file://instance/1684106408' name='1684106408'>java.io.UnixFileSystem#1</a>\n<classLoader>=null\n"};
 
         instance.executeQuery("select map(heap.findClass(\"java.io.File\").statics, \"index + '=' + toHtml(it)\")", new ObjectVisitor() {
 
@@ -688,7 +688,7 @@ public boolean visit(Object o) {
     public void testMapWrapping() throws Exception {
         System.out.println("map wrapping");
 
-        final String[] result = new String[] {"", "<a href='file://class/java.util.HashMap$Entry[]'>class java.util.HashMap$Entry[]</a>"};
+        final String[] result = new String[] {"", "<a href='file://class/1746081976' name='1746081976'>class java.util.HashMap$Entry[]</a>"};
 
         instance.executeQuery("select unique(map(filter(reachables(a), 'it != null'), 'toHtml(it.clazz)')) from instanceof java.util.HashMap a", new ObjectVisitor() {
 
diff --git a/profiler.snaptracer/manifest.mf b/profiler.snaptracer/manifest.mf
index 754139e18..545132d6c 100644
--- a/profiler.snaptracer/manifest.mf
+++ b/profiler.snaptracer/manifest.mf
@@ -2,5 +2,5 @@ Manifest-Version: 1.0
 AutoUpdate-Show-In-Client: false
 OpenIDE-Module: org.netbeans.modules.profiler.snaptracer/1
 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/profiler/snaptracer/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.24
+OpenIDE-Module-Specification-Version: 1.25
 
diff --git a/profiler.snaptracer/nbproject/project.xml b/profiler.snaptracer/nbproject/project.xml
index f1c0bd53f..bf7c025ca 100644
--- a/profiler.snaptracer/nbproject/project.xml
+++ b/profiler.snaptracer/nbproject/project.xml
@@ -58,7 +58,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>1</release-version>
-                        <specification-version>1.128</specification-version>
+                        <specification-version>1.143</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -76,7 +76,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>3</release-version>
-                        <specification-version>3.14</specification-version>
+                        <specification-version>3.29</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -169,7 +169,10 @@
                     </run-dependency>
                 </dependency>
             </module-dependencies>
-            <public-packages/>
+            <friend-packages>
+                <friend>com.sun.tools.visualvm.profiling</friend>
+                <package>org.netbeans.modules.profiler.snaptracer.impl</package>
+            </friend-packages>
         </data>
     </configuration>
 </project>
diff --git a/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/IdeSnapshot.java b/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/IdeSnapshot.java
index c6642cd38..715f94942 100644
--- a/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/IdeSnapshot.java
+++ b/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/IdeSnapshot.java
@@ -43,14 +43,14 @@
  */
 public final class IdeSnapshot {
 
-    private SampledCPUSnapshot cpuSnapshot;
+    private final SampledCPUSnapshot cpuSnapshot;
     private LogReader xmlLogs;
     private LogRecord lastRecord;
     private Map<Integer, LogRecord> recordsMap;
     private Map<Integer, LogRecordInfo> infosMap;
     private final FileObject npssFileObject;
 
-    IdeSnapshot(FileObject npssFO, FileObject uigestureFO) throws IOException {
+    public IdeSnapshot(FileObject npssFO, FileObject uigestureFO) throws IOException {
         cpuSnapshot = new SampledCPUSnapshot(npssFO);
         npssFileObject = npssFO;
         if (uigestureFO != null) {
diff --git a/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerController.java b/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerController.java
index ec0e6c219..6dbb784ad 100644
--- a/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerController.java
+++ b/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerController.java
@@ -52,7 +52,7 @@
         + "not supported in Tracer,<br>all negative values will be"
         + " displayed as 0.</html>"
     })
-final class TracerController  {
+public final class TracerController  {
 
     private static final Logger LOGGER = Logger.getLogger(TracerController.class.getName());
 
@@ -77,7 +77,7 @@
 
     // --- Constructor ---------------------------------------------------------
 
-    TracerController(TracerModel model) {
+    public TracerController(TracerModel model) {
         this.model = model;
 
         changeSupport = new PropertyChangeSupport(this);
diff --git a/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerModel.java b/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerModel.java
index c0ebbff6f..bf967c161 100644
--- a/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerModel.java
+++ b/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerModel.java
@@ -47,7 +47,7 @@
  *
  * @author Jiri Sedlacek
  */
-final class TracerModel {
+public final class TracerModel {
 
     private static final Logger LOGGER = Logger.getLogger(TracerController.class.getName());
 
@@ -63,7 +63,7 @@
 
     // --- Constructor ---------------------------------------------------------
 
-    TracerModel(IdeSnapshot snapshot) {
+    public TracerModel(IdeSnapshot snapshot) {
         this.snapshot = snapshot;
         timelineSupport = new TimelineSupport(new TimelineSupport.DescriptorResolver() {
             public TracerProbeDescriptor getDescriptor(TracerProbe p) {
diff --git a/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerView.java b/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerView.java
index 2af19820a..a89e63923 100644
--- a/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerView.java
+++ b/profiler.snaptracer/src/org/netbeans/modules/profiler/snaptracer/impl/TracerView.java
@@ -50,6 +50,7 @@
 import org.netbeans.modules.profiler.SampledCPUSnapshot;
 import org.netbeans.modules.profiler.actions.CompareSnapshotsAction;
 import org.netbeans.modules.profiler.api.GoToSource;
+import org.netbeans.modules.profiler.v2.ProfilerFeature;
 import org.netbeans.modules.profiler.v2.ProfilerSession;
 import org.openide.util.Exceptions;
 import org.openide.util.NbBundle;
@@ -61,7 +62,7 @@
  * @author Jiri Sedlacek
  * @author Tomas Hurka
  */
-final class TracerView {
+public final class TracerView {
     
     private final TracerModel model;
     private final TracerController controller;
@@ -69,12 +70,12 @@
     private TimelineView timelineView;
     private SnapshotView snapshotView;
     
-    TracerView(TracerModel model, TracerController controller) {
+    public TracerView(TracerModel model, TracerController controller) {
         this.model = model;
         this.controller = controller;
     }
 
-    protected JComponent createComponent() {
+    public JComponent createComponent() {
         
         final JPanel component = new JPanel(new BorderLayout());
 
@@ -323,9 +324,17 @@ void setData(CPUResultsSnapshot snapshot) {
             super.setSnapshot(snapshot, true);
         }
         
-        protected boolean profileMethodSupported() {
+        protected boolean profileMethodEnabled() {
             return false;
         }
+        
+        protected boolean profileMethodSupported() {
+            return ProfilerFeature.Registry.hasProviders();
+        }
+    
+        protected boolean profileClassSupported() {
+            return ProfilerFeature.Registry.hasProviders();
+        }
 
         protected boolean showSourceSupported() {
             return GoToSource.isAvailable();
@@ -354,8 +363,8 @@ public void run() {
         
         protected void customizeNodePopup(DataView invoker, JPopupMenu popup, Object value, ClientUtils.SourceCodeSelection userValue) {
             if (value instanceof PrestimeCPUCCTNode) {
-                popup.addSeparator();
                 popup.add(new FindMethodAction((PrestimeCPUCCTNode)value));
+                popup.addSeparator();
             }
         }
         
diff --git a/profiler/manifest.mf b/profiler/manifest.mf
index 594636058..c10debf51 100644
--- a/profiler/manifest.mf
+++ b/profiler/manifest.mf
@@ -5,5 +5,5 @@ OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/profiler/Bundle.propertie
 OpenIDE-Module-Install: org/netbeans/modules/profiler/ProfilerModule.class
 OpenIDE-Module-Requires: org.openide.windows.WindowManager
 OpenIDE-Module-Package-Dependencies: com.sun.tools.attach[VirtualMachine]
-OpenIDE-Module-Specification-Version: 3.28
+OpenIDE-Module-Specification-Version: 3.30
 
diff --git a/profiler/nbproject/project.xml b/profiler/nbproject/project.xml
index 304827596..b40e60a37 100644
--- a/profiler/nbproject/project.xml
+++ b/profiler/nbproject/project.xml
@@ -67,7 +67,7 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>1</release-version>
-                        <specification-version>1.137</specification-version>
+                        <specification-version>1.145</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -255,6 +255,7 @@
             </test-dependencies>
             <friend-packages>
                 <friend>com.sun.tools.visualvm.core</friend>
+                <friend>com.sun.tools.visualvm.heapdump</friend>
                 <friend>com.sun.tools.visualvm.profiler</friend>
                 <friend>com.sun.tools.visualvm.profiling</friend>
                 <friend>com.sun.tools.visualvm.sampler</friend>
@@ -277,7 +278,6 @@
                 <package>org.netbeans.modules.profiler</package>
                 <package>org.netbeans.modules.profiler.actions</package>
                 <package>org.netbeans.modules.profiler.ui</package>
-                <package>org.netbeans.modules.profiler.ui.panels</package>
                 <package>org.netbeans.modules.profiler.utils</package>
                 <package>org.netbeans.modules.profiler.v2</package>
             </friend-packages>
diff --git a/profiler/src/org/netbeans/modules/profiler/SnapshotResultsWindow.java b/profiler/src/org/netbeans/modules/profiler/SnapshotResultsWindow.java
index 7f56ce72d..ac3691544 100644
--- a/profiler/src/org/netbeans/modules/profiler/SnapshotResultsWindow.java
+++ b/profiler/src/org/netbeans/modules/profiler/SnapshotResultsWindow.java
@@ -53,6 +53,7 @@
 import org.netbeans.modules.profiler.api.ProfilerDialogs;
 import org.netbeans.modules.profiler.api.ProfilerIDESettings;
 import org.netbeans.modules.profiler.api.icons.ProfilerIcons;
+import org.netbeans.modules.profiler.v2.ProfilerFeature;
 import org.netbeans.modules.profiler.v2.ProfilerSession;
 import org.netbeans.spi.actions.AbstractSavable;
 import org.openide.DialogDescriptor;
@@ -506,6 +507,12 @@ private void setupCPUResultsView() {
                 protected boolean showSourceSupported() {
                     return GoToSource.isAvailable();
                 }
+                protected boolean profileMethodSupported() {
+                    return ProfilerFeature.Registry.hasProviders();
+                }
+                protected boolean profileClassSupported() {
+                    return ProfilerFeature.Registry.hasProviders();
+                }
                 protected void showSource(ClientUtils.SourceCodeSelection value) {
                     Lookup.Provider project = snapshot.getProject();
                     String className = value.getClassName();
@@ -562,6 +569,12 @@ private void setupMemoryResultsView() {
                 protected boolean showSourceSupported() {
                     return GoToSource.isAvailable();
                 }
+                protected boolean profileMethodSupported() {
+                    return ProfilerFeature.Registry.hasProviders();
+                }
+                protected boolean profileClassSupported() {
+                    return ProfilerFeature.Registry.hasProviders();
+                }
                 protected void showSource(ClientUtils.SourceCodeSelection value) {
                     Lookup.Provider project = snapshot.getProject();
                     String className = value.getClassName();
diff --git a/profiler/src/org/netbeans/modules/profiler/v2/ProfilerFeature.java b/profiler/src/org/netbeans/modules/profiler/v2/ProfilerFeature.java
index 14a374127..99421691c 100644
--- a/profiler/src/org/netbeans/modules/profiler/v2/ProfilerFeature.java
+++ b/profiler/src/org/netbeans/modules/profiler/v2/ProfilerFeature.java
@@ -19,6 +19,7 @@
 
 package org.netbeans.modules.profiler.v2;
 
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 import javax.swing.Icon;
@@ -240,6 +241,7 @@ protected synchronized final void fireChange() {
         
     }
     
+    
     // --- Provider ------------------------------------------------------------
     
     public static abstract class Provider {
@@ -248,4 +250,25 @@ protected synchronized final void fireChange() {
         
     }
     
+    
+    // --- Registry ------------------------------------------------------------
+    
+    public static final class Registry {
+        
+        private static boolean HAS_PROVIDERS;
+        
+        private Registry() {}
+        
+        public static boolean hasProviders() {
+            return HAS_PROVIDERS;
+        }
+        
+        static Collection<? extends Provider> getProviders() {
+            Collection<? extends Provider> providers = Lookup.getDefault().lookupAll(Provider.class);
+            HAS_PROVIDERS = !providers.isEmpty();
+            return providers;
+        }
+        
+    }
+    
 }
diff --git a/profiler/src/org/netbeans/modules/profiler/v2/ProfilerFeatures.java b/profiler/src/org/netbeans/modules/profiler/v2/ProfilerFeatures.java
index e54b58a93..821fa69b5 100644
--- a/profiler/src/org/netbeans/modules/profiler/v2/ProfilerFeatures.java
+++ b/profiler/src/org/netbeans/modules/profiler/v2/ProfilerFeatures.java
@@ -81,7 +81,7 @@ public int compare(ProfilerFeature f1, ProfilerFeature f2) {
         listeners = new HashSet();
         
         // Populates SessionStorage, can be accessed in EDT from now
-        for (ProfilerFeature.Provider provider : Lookup.getDefault().lookupAll(ProfilerFeature.Provider.class)) {
+        for (ProfilerFeature.Provider provider : ProfilerFeature.Registry.getProviders()) {
             ProfilerFeature feature = provider.getFeature(session);
             if (feature != null) features.add(feature);
 //            features.add(provider.getFeature(session));
diff --git a/profiler/src/org/netbeans/modules/profiler/v2/ProfilerSessions.java b/profiler/src/org/netbeans/modules/profiler/v2/ProfilerSessions.java
index 7a57c27f4..197d55cfc 100644
--- a/profiler/src/org/netbeans/modules/profiler/v2/ProfilerSessions.java
+++ b/profiler/src/org/netbeans/modules/profiler/v2/ProfilerSessions.java
@@ -74,6 +74,7 @@
 @NbBundle.Messages({
     "ProfilerSessions_actionNotSupported=Action not supported by the current profiling session.",
     "ProfilerSessions_loadingFeatures=Loading profiler modes...",
+    "ProfilerSessions_noFeature=<no compatible mode available>",
     "ProfilerSessions_selectProject=&Select the project to profile:",
     "ProfilerSessions_selectFeature=Select Profiler Mode",
     "ProfilerSessions_selectHandlingFeature=S&elect profiler mode to handle the action:",
@@ -400,7 +401,7 @@ private void refreshProfileAttach(Lookup.Provider project) {
         private void refreshFeatures(final Lookup context, final Lookup.Provider project) {
             contents.removeAll();
             
-            JLabel l = new JLabel(Bundle.ProfilerSessions_loadingFeatures(), JLabel.CENTER);
+            final JLabel l = new JLabel(Bundle.ProfilerSessions_loadingFeatures(), JLabel.CENTER);
             GridBagConstraints c = new GridBagConstraints();
             c.gridx = 0;
             c.gridy = 0;
@@ -421,40 +422,44 @@ public void run() {
                     Lookup projectContext = project == null ? Lookup.EMPTY :
                                             Lookups.fixed(project);
                     selectedSession = ProfilerSession.forContext(projectContext);
-                    final ProfilerFeatures features = selectedSession.getFeatures();
+                    final ProfilerFeatures features = selectedSession == null ? null :
+                                                      selectedSession.getFeatures();
                     
                     SwingUtilities.invokeLater(new Runnable() {
                         public void run() {
                             
-                            contents.removeAll();
-                            selectModeLabel.setLabelFor(null);
-                            
-                            int y = 0;
-                            GridBagConstraints c;
-                            
-                            ButtonGroup rbg = new ButtonGroup();
-                            for (final ProfilerFeature f : features.getAvailable()) {
-                                if (f.supportsConfiguration(context)) {
-                                    JRadioButton rb = new JExtendedRadioButton(f.getName(), f.getIcon()) {
-                                        protected void fireItemStateChanged(ItemEvent e) {
-                                            if (e.getStateChange() == ItemEvent.SELECTED)
-                                                selectedFeature = f;
-                                        }
-                                    };
-                                    rbg.add(rb);
-                                    if (rbg.getSelection() == null) rb.setSelected(true);
-                                    c = new GridBagConstraints();
-                                    c.gridx = 0;
-                                    c.gridy = y++;
-                                    c.anchor = GridBagConstraints.WEST;
-                                    c.insets = new Insets(0, 20, 0, 10);
-                                    contents.add(rb, c);
-                                    if (selectModeLabel.getLabelFor() == null) selectModeLabel.setLabelFor(rb);
+                            if (features == null || features.getAvailable().isEmpty()) {
+                                l.setText(Bundle.ProfilerSessions_noFeature());
+                            } else {                            
+                                contents.removeAll();
+                                selectModeLabel.setLabelFor(null);
+
+                                int y = 0;
+                                GridBagConstraints c;
+
+                                ButtonGroup rbg = new ButtonGroup();
+                                for (final ProfilerFeature f : features.getAvailable()) {
+                                    if (f.supportsConfiguration(context)) {
+                                        JRadioButton rb = new JExtendedRadioButton(f.getName(), f.getIcon()) {
+                                            protected void fireItemStateChanged(ItemEvent e) {
+                                                if (e.getStateChange() == ItemEvent.SELECTED)
+                                                    selectedFeature = f;
+                                            }
+                                        };
+                                        rbg.add(rb);
+                                        if (rbg.getSelection() == null) rb.setSelected(true);
+                                        c = new GridBagConstraints();
+                                        c.gridx = 0;
+                                        c.gridy = y++;
+                                        c.anchor = GridBagConstraints.WEST;
+                                        c.insets = new Insets(0, 20, 0, 10);
+                                        contents.add(rb, c);
+                                        if (selectModeLabel.getLabelFor() == null) selectModeLabel.setLabelFor(rb);
+                                    }
                                 }
+
+                                repaintContents();
                             }
-                            
-                            repaintContents();
-                            
                         }
                     });
                     
diff --git a/profiler/src/org/netbeans/modules/profiler/v2/ProfilerWindow.java b/profiler/src/org/netbeans/modules/profiler/v2/ProfilerWindow.java
index 602cd4786..be9ae5293 100644
--- a/profiler/src/org/netbeans/modules/profiler/v2/ProfilerWindow.java
+++ b/profiler/src/org/netbeans/modules/profiler/v2/ProfilerWindow.java
@@ -66,6 +66,7 @@
 import org.netbeans.lib.profiler.ui.swing.PopupButton;
 import org.netbeans.lib.profiler.ui.swing.ProfilerPopup;
 import org.netbeans.lib.profiler.ui.swing.SearchUtils;
+import org.netbeans.lib.profiler.ui.swing.StayOpenPopupMenu;
 import org.netbeans.modules.profiler.ProfilerTopComponent;
 import org.netbeans.modules.profiler.actions.HeapDumpAction;
 import org.netbeans.modules.profiler.actions.RunGCAction;
@@ -83,7 +84,6 @@
 import org.netbeans.modules.profiler.v2.impl.ProfilerStatus;
 import org.netbeans.modules.profiler.v2.impl.WelcomePanel;
 import org.netbeans.modules.profiler.v2.ui.DropdownButton;
-import org.netbeans.modules.profiler.v2.ui.StayOpenPopupMenu;
 import org.netbeans.modules.profiler.v2.ui.ToggleButtonMenuItem;
 import org.openide.filesystems.FileObject;
 import org.openide.util.Exceptions;
diff --git a/profiler/src/org/netbeans/modules/profiler/v2/features/MethodsFeatureUI.java b/profiler/src/org/netbeans/modules/profiler/v2/features/MethodsFeatureUI.java
index 209156c30..5983391be 100644
--- a/profiler/src/org/netbeans/modules/profiler/v2/features/MethodsFeatureUI.java
+++ b/profiler/src/org/netbeans/modules/profiler/v2/features/MethodsFeatureUI.java
@@ -33,6 +33,7 @@
 import org.netbeans.lib.profiler.common.Profiler;
 import org.netbeans.lib.profiler.ui.components.ProfilerToolbar;
 import org.netbeans.lib.profiler.ui.cpu.LiveCPUView;
+import org.netbeans.lib.profiler.ui.cpu.LiveCPUViewUpdater;
 import org.netbeans.lib.profiler.ui.swing.GrayLabel;
 import org.netbeans.lib.profiler.ui.swing.MultiButtonGroup;
 import org.netbeans.modules.profiler.actions.ResetResultsAction;
@@ -70,6 +71,7 @@
     
     private ProfilerToolbar toolbar;
     private LiveCPUView cpuView;
+    private LiveCPUViewUpdater updater;
 
     
     // --- External implementation ---------------------------------------------
@@ -113,11 +115,11 @@ void resetPause() {
     }
     
     void setForceRefresh() {
-        if (cpuView != null) cpuView.setForceRefresh(true);
+        if (updater != null) updater.setForceRefresh(true);
     }
     
     void refreshData() throws ClientUtils.TargetAppOrVMTerminated {
-        if (cpuView != null) cpuView.refreshData();
+        if (updater != null) updater.update();
     }
     
     void resetData() {
@@ -133,7 +135,7 @@ void resetData() {
     
     
     void cleanup() {
-        if (cpuView != null) cpuView.cleanup();
+        if (updater != null) updater.cleanup();
     }
     
     
@@ -162,9 +164,20 @@ private void initUI() {
         // --- Results ---------------------------------------------------------
         
         cpuView = new LiveCPUView(getMethodsSelection()) {
-            protected ProfilerClient getProfilerClient() {
-                return MethodsFeatureUI.this.getProfilerClient();
-            }
+//            protected ProfilerClient getProfilerClient() {
+//                return MethodsFeatureUI.this.getProfilerClient();
+//            }
+////            protected boolean isSampling() {
+////                return MethodsFeatureUI.this.getProfilerClient().getCurrentInstrType() == ProfilerClient.INSTR_NONE_SAMPLING;
+////            }
+////            protected void requestResults() throws ClientUtils.TargetAppOrVMTerminated {
+////                MethodsFeatureUI.this.getProfilerClient().forceObtainedResultsDump(true);
+////            }
+////            protected CPUResultsSnapshot getResults() throws ClientUtils.TargetAppOrVMTerminated, CPUResultsSnapshot.NoDataAvailableException {
+////                ProfilerClient client = MethodsFeatureUI.this.getProfilerClient();
+////                return client.getStatus().getInstrMethodClasses() == null ?
+////                       null : client.getCPUProfilingResultsSnapshot(false);
+////            }
             protected boolean showSourceSupported() {
                 return GoToSource.isAvailable();
             }
@@ -206,6 +219,8 @@ protected void foundInReverseCalls() {
         
         cpuView.putClientProperty("HelpCtx.Key", "ProfileMethods.HelpCtx"); // NOI18N
         
+        updater = new LiveCPUViewUpdater(cpuView, getProfilerClient());
+        
         
         // --- Toolbar ---------------------------------------------------------
         
@@ -214,7 +229,7 @@ protected void foundInReverseCalls() {
         lrPauseButton = new JToggleButton(Icons.getIcon(GeneralIcons.PAUSE)) {
             protected void fireItemStateChanged(ItemEvent event) {
                 boolean paused = isSelected();
-                cpuView.setPaused(paused);
+                updater.setPaused(paused);
                 if (!paused) refreshResults();
                 refreshToolbar(getSessionState());
             }
diff --git a/profiler/src/org/netbeans/modules/profiler/v2/features/ObjectsFeatureUI.java b/profiler/src/org/netbeans/modules/profiler/v2/features/ObjectsFeatureUI.java
index 430ceca50..9434c4f03 100644
--- a/profiler/src/org/netbeans/modules/profiler/v2/features/ObjectsFeatureUI.java
+++ b/profiler/src/org/netbeans/modules/profiler/v2/features/ObjectsFeatureUI.java
@@ -33,6 +33,7 @@
 import org.netbeans.lib.profiler.common.Profiler;
 import org.netbeans.lib.profiler.ui.components.ProfilerToolbar;
 import org.netbeans.lib.profiler.ui.memory.LiveMemoryView;
+import org.netbeans.lib.profiler.ui.memory.LiveMemoryViewUpdater;
 import org.netbeans.lib.profiler.ui.swing.GrayLabel;
 import org.netbeans.modules.profiler.actions.ResetResultsAction;
 import org.netbeans.modules.profiler.actions.TakeSnapshotAction;
@@ -60,6 +61,7 @@
     
     private ProfilerToolbar toolbar;
     private LiveMemoryView memoryView;
+    private LiveMemoryViewUpdater updater;
 
     
     // --- External implementation ---------------------------------------------
@@ -101,11 +103,11 @@ void resetPause() {
     }
     
     void setForceRefresh() {
-        if (memoryView != null) memoryView.setForceRefresh(true);
+        if (updater != null) updater.setForceRefresh(true);
     }
     
     void refreshData() throws ClientUtils.TargetAppOrVMTerminated {
-        if (memoryView != null) memoryView.refreshData();
+        if (updater != null) updater.update();
     }
     
     void resetData() {
@@ -121,7 +123,7 @@ void resetData() {
     
     
     void cleanup() {
-        if (memoryView != null) memoryView.cleanup();
+        if (updater != null) updater.cleanup();
     }
     
     
@@ -177,6 +179,8 @@ protected void popupHidden() {
         
         memoryView.putClientProperty("HelpCtx.Key", "ProfileObjects.HelpCtx"); // NOI18N
         
+        updater = new LiveMemoryViewUpdater(memoryView, getProfilerClient());
+        
         
         // --- Toolbar ---------------------------------------------------------
         
@@ -185,7 +189,7 @@ protected void popupHidden() {
         lrPauseButton = new JToggleButton(Icons.getIcon(GeneralIcons.PAUSE)) {
             protected void fireItemStateChanged(ItemEvent event) {
                 boolean paused = isSelected();
-                memoryView.setPaused(paused);
+                updater.setPaused(paused);
                 if (!paused) refreshResults();
                 refreshToolbar(getSessionState());
             }
diff --git a/profiler/src/org/netbeans/modules/profiler/v2/features/SQLFeatureUI.java b/profiler/src/org/netbeans/modules/profiler/v2/features/SQLFeatureUI.java
index 4eb74bf1a..9bc61796e 100644
--- a/profiler/src/org/netbeans/modules/profiler/v2/features/SQLFeatureUI.java
+++ b/profiler/src/org/netbeans/modules/profiler/v2/features/SQLFeatureUI.java
@@ -37,6 +37,7 @@
 import org.netbeans.lib.profiler.ui.components.HTMLTextArea;
 import org.netbeans.lib.profiler.ui.components.ProfilerToolbar;
 import org.netbeans.lib.profiler.ui.jdbc.LiveJDBCView;
+import org.netbeans.lib.profiler.ui.jdbc.LiveJDBCViewUpdater;
 import org.netbeans.lib.profiler.ui.swing.GrayLabel;
 import org.netbeans.modules.profiler.actions.ResetResultsAction;
 import org.netbeans.modules.profiler.actions.TakeSnapshotAction;
@@ -62,6 +63,8 @@
     
     private ProfilerToolbar toolbar;
     private LiveJDBCView jdbcView;
+    private LiveJDBCViewUpdater updater;
+    
     
     // --- External implementation ---------------------------------------------
         
@@ -90,11 +93,11 @@ JPanel getResultsUI() {
     void sessionStateChanged(int sessionState) {
         refreshToolbar(sessionState);
         
-        if (sessionState == Profiler.PROFILING_INACTIVE || sessionState == Profiler.PROFILING_IN_TRANSITION) {
-            if (jdbcView != null) jdbcView.profilingSessionFinished();
-        } else if (sessionState == Profiler.PROFILING_RUNNING) {
-            if (jdbcView != null) jdbcView.profilingSessionStarted();
-        }
+//        if (sessionState == Profiler.PROFILING_INACTIVE || sessionState == Profiler.PROFILING_IN_TRANSITION) {
+//            if (jdbcView != null) jdbcView.profilingSessionFinished();
+//        } else if (sessionState == Profiler.PROFILING_RUNNING) {
+//            if (jdbcView != null) jdbcView.profilingSessionStarted();
+//        }
     }
 
     void resetPause() {
@@ -102,15 +105,22 @@ void resetPause() {
     }
     
     void setForceRefresh() {
-        if (jdbcView != null) jdbcView.setForceRefresh(true);
+        if (updater != null) updater.setForceRefresh(true);
     }
     
     void refreshData() throws ClientUtils.TargetAppOrVMTerminated {
-        if (jdbcView != null) jdbcView.refreshData();
+        if (updater != null) updater.update();
     }
         
     void resetData() {
-        if (jdbcView != null) jdbcView.resetData();
+        if (lrDeltasButton != null) {
+            lrDeltasButton.setSelected(false);
+            lrDeltasButton.setToolTipText(Bundle.MethodsFeatureUI_showDeltas());
+        }
+        if (jdbcView != null) {
+            jdbcView.resetData();
+            jdbcView.setDiffView(false);
+        }
     }
     
     void cleanup() {
@@ -188,6 +198,8 @@ protected void popupHidden() {
         
         jdbcView.putClientProperty("HelpCtx.Key", "ProfileSQL.HelpCtx"); // NOI18N
         
+        updater = new LiveJDBCViewUpdater(jdbcView, getProfilerClient());
+        
         
         // --- Toolbar ---------------------------------------------------------
         
@@ -196,7 +208,7 @@ protected void popupHidden() {
         lrPauseButton = new JToggleButton(Icons.getIcon(GeneralIcons.PAUSE)) {
             protected void fireItemStateChanged(ItemEvent event) {
                 boolean paused = isSelected();
-                jdbcView.setPaused(paused);
+                updater.setPaused(paused);
                 if (!paused) refreshResults();
                 refreshToolbar(getSessionState());
             }
diff --git a/profiler/src/org/netbeans/modules/profiler/v2/ui/ToggleButtonMenuItem.java b/profiler/src/org/netbeans/modules/profiler/v2/ui/ToggleButtonMenuItem.java
index 21aef8ae3..ee7126862 100644
--- a/profiler/src/org/netbeans/modules/profiler/v2/ui/ToggleButtonMenuItem.java
+++ b/profiler/src/org/netbeans/modules/profiler/v2/ui/ToggleButtonMenuItem.java
@@ -33,6 +33,7 @@
 import javax.swing.JToolBar;
 import javax.swing.UIManager;
 import org.netbeans.lib.profiler.ui.UIUtils;
+import org.netbeans.lib.profiler.ui.swing.StayOpenPopupMenu;
 
 /**
  *


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists