561776: Improve handling of FINALIZABLE and UNFINALIZED

Changes to the DTFJ parser and queries to take advantage.

Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=561776

Change-Id: I7fc7e2a4e4e721c02fa79ddbd1f0d7b6e468cde7
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/FindLeaksQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/FindLeaksQuery.java
index b567649..d6df651 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/FindLeaksQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/FindLeaksQuery.java
@@ -47,6 +47,7 @@
 import org.eclipse.mat.snapshot.IMultiplePathsFromGCRootsComputer;

 import org.eclipse.mat.snapshot.ISnapshot;

 import org.eclipse.mat.snapshot.MultiplePathsFromGCRootsRecord;

+import org.eclipse.mat.snapshot.model.GCRootInfo;

 import org.eclipse.mat.snapshot.model.IClass;

 import org.eclipse.mat.snapshot.model.IObject;

 import org.eclipse.mat.util.IProgressListener;

@@ -69,6 +70,8 @@
 

     private final static Set<String> REFERENCE_FIELD_SET = new HashSet<String>(Arrays

                     .asList(new String[] { "referent" })); //$NON-NLS-1$

+    private final static Set<String> UNFINALIZED_REFERENCE_FIELD_SET = new HashSet<String>(Arrays

+                    .asList(new String[] { "<" + GCRootInfo.getTypeAsString(GCRootInfo.Type.UNFINALIZED) + ">" })); //$NON-NLS-1$ //$NON-NLS-2$

     private final static int MAX_DEPTH = 1000;

 

     // ////////////////////////////////////////////

@@ -237,6 +240,13 @@
             {

                 excludeMap.put(clazz, REFERENCE_FIELD_SET);

             }

+        // Unfinalized objects from J9

+        classes = snapshot.getClassesByName("java.lang.Runtime", false); //$NON-NLS-1$

+        if (classes != null)

+            for (IClass clazz : classes)

+            {

+                excludeMap.put(clazz, UNFINALIZED_REFERENCE_FIELD_SET);

+            }

 

         IMultiplePathsFromGCRootsComputer comp = snapshot.getMultiplePathsFromGCRoots(objectIds, excludeMap);

 

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/FindLeaksQuery2.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/FindLeaksQuery2.java
index de2c8a3..f064e6e 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/FindLeaksQuery2.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/FindLeaksQuery2.java
@@ -55,6 +55,7 @@
 import org.eclipse.mat.snapshot.IMultiplePathsFromGCRootsComputer;

 import org.eclipse.mat.snapshot.ISnapshot;

 import org.eclipse.mat.snapshot.MultiplePathsFromGCRootsRecord;

+import org.eclipse.mat.snapshot.model.GCRootInfo;

 import org.eclipse.mat.snapshot.model.IClass;

 import org.eclipse.mat.snapshot.model.IObject;

 import org.eclipse.mat.snapshot.query.SnapshotQuery;

@@ -78,6 +79,8 @@
 

     private final static Set<String> REFERENCE_FIELD_SET = new HashSet<String>(Arrays

                     .asList(new String[] { "referent" })); //$NON-NLS-1$

+    private final static Set<String> UNFINALIZED_REFERENCE_FIELD_SET = new HashSet<String>(Arrays

+                    .asList(new String[] { "<" + GCRootInfo.getTypeAsString(GCRootInfo.Type.UNFINALIZED) + ">" })); //$NON-NLS-1$ //$NON-NLS-2$

     private final static int MAX_DEPTH = 1000;

 

     // ////////////////////////////////////////////

@@ -424,6 +427,13 @@
             {

                 excludeMap.put(clazz, REFERENCE_FIELD_SET);

             }

+        // Unfinalized objects from J9

+        classes = snapshot.getClassesByName("java.lang.Runtime", false); //$NON-NLS-1$

+        if (classes != null)

+            for (IClass clazz : classes)

+            {

+                excludeMap.put(clazz, UNFINALIZED_REFERENCE_FIELD_SET);

+            }

 

         IMultiplePathsFromGCRootsComputer comp = snapshot.getMultiplePathsFromGCRoots(objectIds, excludeMap);

 

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/LeakHunterQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/LeakHunterQuery.java
index 5615d7b..3ee1561 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/LeakHunterQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/LeakHunterQuery.java
@@ -1058,6 +1058,10 @@
             //named argument after -excludes

             sb.append(" -excludes java.lang.ref.WeakReference:"); //$NON-NLS-1$

             sb.append("referent"); //$NON-NLS-1$

+            sb.append(" -excludes java.lang.ref.Finalizer:"); //$NON-NLS-1$

+            sb.append("referent"); //$NON-NLS-1$

+            sb.append(" -excludes java.lang.Runtime:"); //$NON-NLS-1$

+            sb.append("<" + GCRootInfo.getTypeAsString(GCRootInfo.Type.UNFINALIZED) + ">"); //$NON-NLS-1$ //$NON-NLS-2$

             sb.append(" -groupby FROM_GC_ROOTS"); //$NON-NLS-1$

             for (int objId : objectIds)

             {

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/ReferenceQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/ReferenceQuery.java
index 06c9f79..b861b2c 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/ReferenceQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/ReferenceQuery.java
@@ -73,7 +73,16 @@
                     IInstance obj = (IInstance) o;

                     ObjectReference ref = getReferent(obj, referent_attribute);

                     if (ref != null)

-                        referentSet.add(ref.getObjectId());

+                    {

+                        try

+                        {

+                            referentSet.add(ref.getObjectId());

+                        }

+                        catch (SnapshotException e)

+                        {

+                            listener.sendUserMessage(IProgressListener.Severity.WARNING, MessageUtil.format(Messages.ReferenceQuery_MissingReferentObject, Long.toHexString(ref.getObjectAddress()), Long.toHexString(o.getObjectAddress()), referent_attribute), e);

+                        }

+                    }

                 }

             }

             if (listener.isCanceled())

@@ -113,7 +122,16 @@
 

                 ObjectReference ref = getReferent(obj);

                 if (ref != null)

-                    referentSet.add(ref.getObjectId());

+                {

+                    try

+                    {

+                        referentSet.add(ref.getObjectId());

+                    }

+                    catch (SnapshotException e)

+                    {

+                        listener.sendUserMessage(IProgressListener.Severity.WARNING, MessageUtil.format(Messages.ReferenceQuery_MissingReferentObject, Long.toHexString(ref.getObjectAddress()), Long.toHexString(obj.getObjectAddress()), DEFAULT_REFERENT), e);

+                    }

+                }

             }

 

             if (listener.isCanceled())

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/component/ComponentReportQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/component/ComponentReportQuery.java
index e8ed611..dea9a37 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/component/ComponentReportQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/component/ComponentReportQuery.java
@@ -15,11 +15,8 @@
 import java.util.ArrayList;

 import java.util.Collection;

 import java.util.Collections;

-import java.util.HashMap;

 import java.util.List;

 import java.util.Locale;

-import java.util.Map;

-import java.util.Set;

 

 import org.eclipse.mat.SnapshotException;

 import org.eclipse.mat.collect.ArrayInt;

@@ -54,7 +51,9 @@
 import org.eclipse.mat.snapshot.model.IClass;

 import org.eclipse.mat.snapshot.model.IInstance;

 import org.eclipse.mat.snapshot.model.IObject;

+import org.eclipse.mat.snapshot.model.NamedReference;

 import org.eclipse.mat.snapshot.model.ObjectReference;

+import org.eclipse.mat.snapshot.model.ThreadToLocalReference;

 import org.eclipse.mat.snapshot.query.IHeapObjectArgument;

 import org.eclipse.mat.snapshot.query.PieFactory;

 import org.eclipse.mat.snapshot.query.RetainedSizeDerivedData;

@@ -82,6 +81,8 @@
 

     private static final String HTML_BREAK = "<br>"; //$NON-NLS-1$

 

+    private static final String UNFINALIZED_REFERENCE = "<" + GCRootInfo.getTypeAsString(GCRootInfo.Type.UNFINALIZED) + ">"; //$NON-NLS-1$ //$NON-NLS-2$

+

     public IResult execute(IProgressListener listener) throws Exception

     {

         SectionSpec componentReport = new SectionSpec(MessageUtil.format(Messages.ComponentReportQuery_ComponentReport,

@@ -230,6 +231,7 @@
         List<ExcludedReferencesDescriptor> excludes = new ArrayList<ExcludedReferencesDescriptor>();

 

         addExcludes(excludes, "java.lang.ref.Finalizer", "referent"); //$NON-NLS-1$ //$NON-NLS-2$

+        addExcludes(excludes, "java.lang.Runtime", UNFINALIZED_REFERENCE); //$NON-NLS-1$

         addExcludes(excludes, "java.lang.ref.PhantomReference", "referent"); //$NON-NLS-1$ //$NON-NLS-2$

         addExcludes(excludes, "java.lang.ref.WeakReference", "referent"); //$NON-NLS-1$ //$NON-NLS-2$

         addExcludes(excludes, "java.lang.ref.SoftReference", "referent"); //$NON-NLS-1$ //$NON-NLS-2$

@@ -398,6 +400,7 @@
                 String command = "customized_retained_set"; //$NON-NLS-1$

                 command += " " + objectLabel; //$NON-NLS-1$

                 command += " -x java.lang.ref.Finalizer:referent"; //$NON-NLS-1$

+                command += " java.lang.Runtime:" + UNFINALIZED_REFERENCE; //$NON-NLS-1$

                 command += " java.lang.ref.PhantomReference:referent"; //$NON-NLS-1$

                 command += " java.lang.ref.WeakReference:referent"; //$NON-NLS-1$

                 command += " java.lang.ref.SoftReference:referent"; //$NON-NLS-1$

@@ -1015,12 +1018,11 @@
     private void addFinalizerStatistic(SectionSpec componentReport, int[] retained, Ticks ticks)

                     throws SnapshotException

     {

+        boolean foundSomeFinalizers = false;

         Collection<IClass> classes = snapshot.getClassesByName("java.lang.ref.Finalizer", true); //$NON-NLS-1$

-        if (classes == null || classes.isEmpty())

+        if (classes == null)

         {

-            addEmptyResult(componentReport, Messages.ComponentReportQuery_FinalizerStatistics,

-                            Messages.ComponentReportQuery_Msg_NoFinalizerObjects);

-            return;

+            classes = Collections.emptyList();

         }

 

         // Currently SetInt uses a lot of memory - 32+8 bits @ 75% capacity = 53 bits per item

@@ -1054,6 +1056,7 @@
                 if (ticks.isCanceled())

                     break;

                 IInstance obj = (IInstance) snapshot.getObject(objectId);

+                foundSomeFinalizers = true;

 

                 ObjectReference ref = ReferenceQuery.getReferent(obj);

                 if (ref != null)

@@ -1067,6 +1070,42 @@
             }

         }

 

+        if (!foundSomeFinalizers)

+        {

+            // Find J9 unfinalized objects via java.lang.Runtime

+            classes = snapshot.getClassesByName("java.lang.Runtime", false); //$NON-NLS-1$

+            if (classes != null)

+            {

+                for (IClass c : classes)

+                {

+                    for (int objectId : c.getObjectIds())

+                    {

+                        if (ticks.isCanceled())

+                            break;

+                        IInstance obj = (IInstance) snapshot.getObject(objectId);

+                        for (NamedReference nr : obj.getOutboundReferences())

+                        {

+                            if (nr instanceof ThreadToLocalReference)

+                            {

+                                ThreadToLocalReference pr = (ThreadToLocalReference)nr;

+                                for (GCRootInfo rootInfo: pr.getGcRootInfo())

+                                {

+                                    if (rootInfo.getType() == GCRootInfo.Type.UNFINALIZED)

+                                    {

+                                        foundSomeFinalizers = true;

+                                        int referentId = rootInfo.getObjectId();

+                                        if (useBits ? retainedIds.get(referentId) : retainedSet.contains(referentId))

+                                        {

+                                            finalizers.add(referentId);

+                                        }

+                                    }

+                                }

+                            }

+                        }

+                    }

+                }

+            }

+        }

         // Add other objects marked as not yet finalized

         for (int root : snapshot.getGCRoots())

         {

@@ -1076,6 +1115,7 @@
                 if (rootInfo.getType() == GCRootInfo.Type.UNFINALIZED

                                 || rootInfo.getType() == GCRootInfo.Type.FINALIZABLE)

                 {

+                    foundSomeFinalizers = true;

                     int referentId = rootInfo.getObjectId();

                     if (useBits ? retainedIds.get(referentId) : retainedSet.contains(referentId))

                     {

@@ -1086,6 +1126,14 @@
             }

         }

 

+        if (!foundSomeFinalizers)

+        {

+            // No finalizers found anywhere, not just for this component

+            addEmptyResult(componentReport, Messages.ComponentReportQuery_FinalizerStatistics,

+                            Messages.ComponentReportQuery_Msg_NoFinalizerObjects);

+            return;

+        }

+

         if (finalizers.isEmpty())

         {

             addEmptyResult(componentReport, Messages.ComponentReportQuery_FinalizerStatistics,

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java
index 142e7a2..88f69c7 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java
@@ -512,6 +512,8 @@
 

     public static String ReferenceQuery_ErrorMsg_NoMatchingClassesFound;

     public static String ReferenceQuery_HistogramOfReferentObjects;

+    public static String ReferenceQuery_MissingReferentObject;

+

     public static String ReferenceQuery_Msg_ComputingReferentSet;

     public static String ReferenceQuery_Msg_ComputingRetainedSet;

     public static String ReferenceQuery_Msg_ComputingStronglyRetainedSet;

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties
index bc44628..7174dc9 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties
@@ -446,6 +446,7 @@
 ReferenceLeakQuery_PathToReferent=Path to referent

 ReferenceLeakQuery_TwoPaths=Two paths to referent {0} from reference object {1}

 ReferenceQuery_ErrorMsg_NoMatchingClassesFound=No classes matching pattern {0}

+ReferenceQuery_MissingReferentObject=Missing referent object at 0x{0} from reference 0x{1} {2}

 ReferenceQuery_Msg_ComputingReferentSet=Computing Referent Set (objects referenced by the Reference objects)...

 ReferenceQuery_Msg_ComputingRetainedSet=Computing retained set of reference set (assuming only the referents are no longer referenced by the Reference objects)...

 ReferenceQuery_Msg_ComputingStronglyRetainedSet=Computing strongly retained set of reference set...

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/MultiplePath2GCRootsQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/MultiplePath2GCRootsQuery.java
index 6965390..c0577d7 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/MultiplePath2GCRootsQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/MultiplePath2GCRootsQuery.java
@@ -52,13 +52,13 @@
 @CommandName("merge_shortest_paths")

 @Icon("/META-INF/icons/mpaths_from_gc.gif")

 @Menu( { @Entry(options = "-excludes \"\""), //

-                @Entry(options = "-excludes java.lang.ref.WeakReference:referent"), //

+                @Entry(options = "-excludes java.lang.ref.WeakReference:referent java.lang.ref.Finalizer:referent java.lang.Runtime:<Unfinalized>"), //

                 @Entry(options = "-excludes java.lang.ref.SoftReference:referent"), //

                 @Entry(options = "-excludes java.lang.ref.PhantomReference:referent"), //

-                @Entry(options = "-excludes java.lang.ref.WeakReference:referent java.lang.ref.SoftReference:referent"), //

+                @Entry(options = "-excludes java.lang.ref.WeakReference:referent java.lang.ref.Finalizer:referent java.lang.Runtime:<Unfinalized> java.lang.ref.SoftReference:referent"), //

                 @Entry(options = "-excludes java.lang.ref.PhantomReference:referent java.lang.ref.SoftReference:referent"), //

-                @Entry(options = "-excludes java.lang.ref.PhantomReference:referent java.lang.ref.WeakReference:referent"), //

-                @Entry(options = "-excludes java.lang.ref.Reference:referent") //

+                @Entry(options = "-excludes java.lang.ref.PhantomReference:referent java.lang.ref.WeakReference:referent java.lang.ref.Finalizer:referent java.lang.Runtime:<Unfinalized>"), //

+                @Entry(options = "-excludes java.lang.ref.Reference:referent java.lang.Runtime:<Unfinalized>") //

 })

 public class MultiplePath2GCRootsQuery implements IQuery

 {

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/Path2GCRootsQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/Path2GCRootsQuery.java
index 378f892..a6ef7c7 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/Path2GCRootsQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/snapshot/inspections/Path2GCRootsQuery.java
@@ -51,13 +51,13 @@
 @CommandName("path2gc")

 @Icon("/META-INF/icons/path2gc.gif")

 @Menu( { @Entry(options = "-excludes \"\""), //

-                @Entry(options = "-excludes java.lang.ref.WeakReference:referent"), //

+                @Entry(options = "-excludes java.lang.ref.WeakReference:referent java.lang.ref.Finalizer:referent java.lang.Runtime:<Unfinalized>"), //

                 @Entry(options = "-excludes java.lang.ref.SoftReference:referent"), //

                 @Entry(options = "-excludes java.lang.ref.PhantomReference:referent"), //

-                @Entry(options = "-excludes java.lang.ref.WeakReference:referent java.lang.ref.SoftReference:referent"), //

-                @Entry(options = "-excludes java.lang.ref.PhantomReference:referent java.lang.ref.SoftReference:referent"), //                

-                @Entry(options = "-excludes java.lang.ref.PhantomReference:referent java.lang.ref.WeakReference:referent"), //

-                @Entry(options = "-excludes java.lang.ref.Reference:referent") //

+                @Entry(options = "-excludes java.lang.ref.WeakReference:referent java.lang.ref.Finalizer:referent java.lang.Runtime:<Unfinalized> java.lang.ref.SoftReference:referent"), //

+                @Entry(options = "-excludes java.lang.ref.PhantomReference:referent java.lang.ref.SoftReference:referent"), //

+                @Entry(options = "-excludes java.lang.ref.PhantomReference:referent java.lang.ref.WeakReference:referent java.lang.ref.Finalizer:referent java.lang.Runtime:<Unfinalized>"), //

+                @Entry(options = "-excludes java.lang.ref.Reference:referent java.lang.Runtime:<Unfinalized>") //

 })

 @HelpUrl("/org.eclipse.mat.ui.help/reference/inspections/path_to_gc_roots.html")

 public class Path2GCRootsQuery implements IQuery

diff --git a/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJIndexBuilder.java b/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJIndexBuilder.java
index 303bf13..5f3a9fc 100644
--- a/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJIndexBuilder.java
+++ b/plugins/org.eclipse.mat.dtfj/src/org/eclipse/mat/dtfj/DTFJIndexBuilder.java
@@ -32,6 +32,7 @@
 import java.util.LinkedHashMap;

 import java.util.LinkedHashSet;

 import java.util.List;

+import java.util.Locale;

 import java.util.Map;

 import java.util.NoSuchElementException;

 import java.util.Set;

@@ -76,10 +77,10 @@
 import org.eclipse.mat.snapshot.model.FieldDescriptor;

 import org.eclipse.mat.snapshot.model.GCRootInfo;

 import org.eclipse.mat.snapshot.model.IObject;

+import org.eclipse.mat.snapshot.model.IObject.Type;

 import org.eclipse.mat.snapshot.model.ObjectReference;

 import org.eclipse.mat.util.IProgressListener;

 import org.eclipse.mat.util.IProgressListener.Severity;

-import org.eclipse.mat.util.MessageUtil;

 

 import com.ibm.dtfj.image.CorruptData;

 import com.ibm.dtfj.image.CorruptDataException;

@@ -914,7 +915,8 @@
 

         try

         {

-            ifo.setJvmInfo(dtfjInfo.getJavaRuntime().getVersion());

+            String version = dtfjInfo.getJavaRuntime().getVersion();

+            ifo.setJvmInfo(version);

             listener.sendUserMessage(Severity.INFO, MessageFormat.format(Messages.DTFJIndexBuilder_JVMVersion, ifo

                             .getJvmInfo()), null);

         }

@@ -1218,7 +1220,7 @@
                 // Scan stack frames for pseudo-classes

                 int frameId = 0;

                 long prevFrameAddress = 0;

-                for (Iterator<?> ii = th.getStackFrames(); ii.hasNext(); ++frameId)

+                for (Iterator<?> ii = getStackFrames(th); ii.hasNext(); ++frameId)

                 {

                     Object next2 = ii.next();

                     if (isCorruptData(next2, listener, Messages.DTFJIndexBuilder_CorruptDataReadingJavaStackFrames,

@@ -1275,7 +1277,7 @@
                                     }

                                     listener.sendUserMessage(Severity.WARNING, MessageFormat.format(

                                                     Messages.DTFJIndexBuilder_DuplicateJavaStackFrame, frameId,

-                                                    format(frameAddress), newMethodName, oldMethodName,

+                                                    format(frameAddress), oldMethodName, newMethodName,

                                                     format(threadAddress)), null);

                                 }

                             }

@@ -2467,7 +2469,7 @@
         out.print("Thread "); //$NON-NLS-1$

         long threadAddress = getThreadAddress(th, null);

         out.println(threadAddress != 0  ? "0x"+Long.toHexString(threadAddress) : "<unknown>"); //$NON-NLS-1$ //$NON-NLS-2$

-        for (Iterator<?> it = th.getStackFrames(); it.hasNext(); out.println())

+        for (Iterator<?> it = getStackFrames(th); it.hasNext(); out.println())

         {

             Object next = it.next();

             out.print(" at "); //$NON-NLS-1$

@@ -2853,7 +2855,7 @@
                     int frameNum = 0;

                     // We need to look ahead to get the frame size

                     Object nextFrame = null;

-                    for (Iterator<?> ii = th.getStackFrames(); nextFrame != null || ii.hasNext(); ++frameNum)

+                    for (Iterator<?> ii = getStackFrames(th); nextFrame != null || ii.hasNext(); ++frameNum)

                     {

                         // Use the lookahead frame if available

                         Object next2;

@@ -3030,6 +3032,28 @@
     }

 

     /**

+     * Safely get stack frames.

+     * Sometimes reading OpenJ9 with IBM DTFJ or vice versa gives problems. 

+     */

+    private Iterator<?> getStackFrames(JavaThread th)

+    {

+        Iterator<?> ii;

+        try

+        {

+            ii = th.getStackFrames();

+        }

+        catch (ExceptionInInitializerError e)

+        {

+            ii = Collections.EMPTY_LIST.iterator();

+        }

+        catch (NoClassDefFoundError e)

+        {

+            ii = Collections.EMPTY_LIST.iterator();

+        }

+        return ii;

+    }

+

+    /**

      * Set the size of the stack frame type.

      * We can only have one size as the frame type instance size, 

      * so choose the first sensible size.

@@ -3431,9 +3455,18 @@
                             Messages.DTFJIndexBuilder_ProblemGettingObjectSize, format(objAddr)), e);

             // Try to cope with bad sizes - at least register an instance of

             // this class

-            cls.addInstance(0);

+            // Use a non-zero size for arrays so that ISnapshot.isArray() works

+            long size = cls.isArrayType() ? 8 : 0;

+            cls.addInstance(size);

             if (cls.getHeapSizePerInstance() == -1)

-                cls.setHeapSizePerInstance(0);

+                cls.setHeapSizePerInstance(size);

+            // Bytes, not elements

+            indexToSize.set(objId, size);

+            if (debugInfo)

+            {

+                // For calculating purge sizes

+                objectToSize2.set(objId, size);

+            }

         }

 

         // To accumulate the outbound refs

@@ -4182,6 +4215,8 @@
                     // The object will in the end need to be finalized, but is

                     // currently in use

                     type = GCRootInfo.Type.UNFINALIZED;

+                    // No need to guess

+                    foundFinalizableGCRoots = true;

                     break;

                 case JavaReference.HEAP_ROOT_CLASSLOADER:

                     type = GCRootInfo.Type.SYSTEM_CLASS;

@@ -4219,6 +4254,11 @@
                 case JavaReference.REACHABILITY_STRONG:

                     break;

                 case JavaReference.REACHABILITY_WEAK:

+                    if (type == GCRootInfo.Type.UNFINALIZED)

+                        threadRoot = true;

+                    else if (skipWeakRoots)

+                        return;

+                    break;

                 case JavaReference.REACHABILITY_SOFT:

                 case JavaReference.REACHABILITY_PHANTOM:

                     if (skipWeakRoots)

@@ -4319,7 +4359,33 @@
                 }

                 else if (so instanceof JavaRuntime)

                 {

-                    // Not expected, but J9 DTFJ returns this

+                    JavaRuntime jr = (JavaRuntime)so;

+                    if (type == GCRootInfo.Type.UNFINALIZED)

+                    {

+                        // Attach finalizers to the runtime

+                        ClassImpl cls = findClassFromName("java.lang.Runtime", listener); //$NON-NLS-1$

+                        if (cls != null)

+                        {

+                            for (Field f : cls.getStaticFields())

+                            {

+                                if (f.getType() == Type.OBJECT)

+                                {

+                                    Object n = f.getValue();

+                                    if (f.getValue() instanceof ObjectReference)

+                                    {

+                                        // Instance which is of this type

+                                        ObjectReference io = (ObjectReference)n;

+                                        // Haven't got an address to id to class mapping yet to check the type

+                                        if (f.getName().toLowerCase(Locale.ENGLISH).contains("runtime")) //$NON-NLS-1$

+                                        {

+                                            source = io.getObjectAddress();

+                                        }

+                                    }

+                                }

+                            }

+                        }

+                    }

+                    // Not expected, but J9 DTFJ returns this e.g. for unfinalized objects

                     if (debugInfo) debugPrint("Unexpected source " + so); //$NON-NLS-1$

                 }

                 else if (so == null)

@@ -5128,7 +5194,7 @@
             if (nJavaRuntimes > 1) {

                 // Prompt for selection

                 // Thrown an exception back to the UI to allow selection

-                MultipleSnapshotsException multipleRuntimeException = new MultipleSnapshotsException(MessageUtil.format(Messages.DTFJIndexBuilder_JavaRuntimesFound, nJavaRuntimes));

+                MultipleSnapshotsException multipleRuntimeException = new MultipleSnapshotsException(MessageFormat.format(Messages.DTFJIndexBuilder_JavaRuntimesFound, nJavaRuntimes));

                 for (MultipleSnapshotsException.Context runtime : runtimes)

                 {

                     multipleRuntimeException.addContext(runtime);

@@ -7884,9 +7950,16 @@
         try

         {

             name = javaClass.getName();

+            int ix = name.indexOf(0);

+            if (ix >= 0)

+                name = null;

         }

         catch (CorruptDataException e)

         {

+            name = null;

+        }

+        if (name == null)

+        {

             long id = getClassAddress(javaClass, listen);

             name = "corruptClassName@" + format(id); //$NON-NLS-1$

             try

diff --git a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/QueriesTest.java b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/QueriesTest.java
index ee278e6..7b06bd7 100644
--- a/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/QueriesTest.java
+++ b/plugins/org.eclipse.mat.tests/src/org/eclipse/mat/tests/snapshot/QueriesTest.java
@@ -1255,7 +1255,7 @@
     {

         ISnapshot snapshot1 = TestSnapshots.getSnapshot(TestSnapshots.IBM_JDK7_64BIT_SYSTEM, false); // Do not dispose this as shared

         ISnapshot snapshot2 = TestSnapshots.getSnapshot(TestSnapshots.IBM_JDK8_64BIT_SYSTEM, false); // Do not dispose this as shared

-        testLeakHunter2Report(snapshot1, snapshot2, 6, 1, 18);

+        testLeakHunter2Report(snapshot1, snapshot2, 5, 1, 16);

     }

 

     /**

diff --git a/plugins/org.eclipse.mat.ui.help/tasks/analyzingfinalizer.dita b/plugins/org.eclipse.mat.ui.help/tasks/analyzingfinalizer.dita
index e67ced1..0102940 100644
--- a/plugins/org.eclipse.mat.ui.help/tasks/analyzingfinalizer.dita
+++ b/plugins/org.eclipse.mat.ui.help/tasks/analyzingfinalizer.dita
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>

 <!--

-    Copyright (c) 2008, 2010 SAP AG.

+    Copyright (c) 2008, 2020 SAP AG.

     All rights reserved. This program and the accompanying materials

     are made available under the terms of the Eclipse Public License v1.0

     which accompanies this distribution, and is available at

@@ -8,6 +8,7 @@
    

     Contributors:

         SAP AG - initial API and implementation

+        Andrew Johnson - J9 finalization 

  -->

 <!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "task.dtd" >

 <task id="task_analyzingfinalizer" xml:lang="en-us">

@@ -16,7 +17,7 @@
 		<copyright>

 			<copyryear year=""></copyryear>

 			<copyrholder>

-				Copyright (c) 2008, 2010 SAP AG and others.

+				Copyright (c) 2008, 2020 SAP AG and others.

 			    All rights reserved. This program and the accompanying materials

 			    are made available under the terms of the Eclipse Public License v1.0

 			    which accompanies this distribution, and is available at

@@ -46,8 +47,8 @@
 								<b>Finalizer in Processing</b>

 							</entry>

 							<entry>

-								<p> Extract object currently processed by Finalizer Thread. This

-									query return the currently processed object by the Finalizer

+								<p> Extract the object currently processed by Finalizer Thread. This

+									query returns the currently processed object by the Finalizer

 									Thread if any. The returned object can be processed for one of

 									the following reasons:</p>

 								<ul>

@@ -72,13 +73,30 @@
 									<li> The application made use of too many objects with

 										finalize() which are queued up in memory.</li>

 								</ul>

-								<p>Note: On J9 JVM-based dumps (e.g. IBM Java, OpenJ9), this

-								   list shows objects waiting to be finalized and those

+								<note>

+								   On J9 JVM-based dumps (e.g. IBM Java, OpenJ9), as expected, this

+								   list shows objects waiting to be finalized. On previous versions

+								   of Memory Analyzer (1.10 and earlier) it also showed those

 								   which have already been finalized and are waiting to be

-								   garbage collected. This query does not provide a way to

-								   differentiate the two (because DTFJ doesn't), although you may 

-								   be able to differentiate based on object fields

-								   (e.g. a "closed" boolean field, etc.).</p>

+								   garbage collected. Those objects where finalization has been

+								   completed are not now shown by this query, but might be in

+								   the unreachable objects histogram if they are waiting to be

+								   reclaimed.

+								   <p>States</p>

+								   <ol>

+								   <li>Initially objects with a finalize method start in the

+								   <xref href="../concepts/gcroots.dita">UNFINALIZED</xref> state.

+								   With a J9 heap dump this is visible as an inbound <codeph>&lt;Unfinalized&gt;</codeph> reference from

+								   the <codeph>java.lang.Runtime</codeph> object.</li>

+								   <li>Once the object is no longer <xref href="../concepts/reachability.dita">reachable</xref>

+								   it goes onto the finalizer queue, visible via this query and with

+								   J9 heap dumps as a <xref href="../concepts/gcroots.dita">FINALIZABLE GC root</xref>.</li>

+								   <li>Once finalization is completed the object is not seen in either of these two

+								   ways. Normally it would be garbage collected, but if the <codeph>finalizer()</codeph>

+								   code made the object reachable again by storing a reference to it in an ordinary object

+								   it might still be strongly reachable.</li>

+								   </ol>

+								</note>

 								<p> Additionally a class-level summary of the objects is provided</p>

 							</entry>

 						</row>

@@ -97,7 +115,7 @@
 								<p>

 									This query shows the thread locals of the daemon thread that

 									performs the object finalizations. If there are any, this

-									indicates miss-use in at least one of the processed finalizers

+									indicates misuse in at least one of the processed finalizers

 									(

 									<cmdname>finalize()</cmdname>

 									implemented wrong) and might cause severe problems (e.g.

@@ -110,6 +128,9 @@
 					</tbody>

 				</tgroup>

 			</table>

+			<p>The <xref href="../reference/inspections/component_report.dita#ref_inspections_component_report__finalizer">component report</xref> also includes information about objects

+			belonging to the component which implement the <codeph>finalize()</codeph> method

+			and so could be finalized at some point.</p>

 		</context>

 	</taskbody>

 </task>
\ No newline at end of file
diff --git a/plugins/org.eclipse.mat.ui.help/tasks/analyzingfinalizer.html b/plugins/org.eclipse.mat.ui.help/tasks/analyzingfinalizer.html
index 7d091e5..3c5ce6b 100644
--- a/plugins/org.eclipse.mat.ui.help/tasks/analyzingfinalizer.html
+++ b/plugins/org.eclipse.mat.ui.help/tasks/analyzingfinalizer.html
@@ -6,8 +6,8 @@
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <meta name="DC.Type" content="task"/>
 <meta name="DC.Title" content="Analyzing Finalizer"/>
-<meta name="copyright" content="Copyright (c) 2008, 2010 SAP AG and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html " type="primary"/>
-<meta name="DC.Rights.Owner" content="Copyright (c) 2008, 2010 SAP AG and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html " type="primary"/>
+<meta name="copyright" content="Copyright (c) 2008, 2020 SAP AG and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html " type="primary"/>
+<meta name="DC.Rights.Owner" content="Copyright (c) 2008, 2020 SAP AG and others. All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html " type="primary"/>
 <meta name="DC.Format" content="XHTML"/>
 <meta name="DC.Identifier" content="task_analyzingfinalizer"/>
 <meta name="DC.Language" content="en-us"/>
@@ -43,8 +43,8 @@
 							</td>
 
 							<td class="entry" valign="top">
-								<p class="p"> Extract object currently processed by Finalizer Thread. This
-									query return the currently processed object by the Finalizer
+								<p class="p"> Extract the object currently processed by Finalizer Thread. This
+									query returns the currently processed object by the Finalizer
 									Thread if any. The returned object can be processed for one of
 									the following reasons:</p>
 
@@ -82,13 +82,35 @@
 
 								</ul>
 
-								<p class="p">Note: On J9 JVM-based dumps (e.g. IBM Java, OpenJ9), this
-								   list shows objects waiting to be finalized and those
+								<div class="note note"><span class="notetitle">Note:</span> 
+								   On J9 JVM-based dumps (e.g. IBM Java, OpenJ9), as expected, this
+								   list shows objects waiting to be finalized. On previous versions
+								   of Memory Analyzer (1.10 and earlier) it also showed those
 								   which have already been finalized and are waiting to be
-								   garbage collected. This query does not provide a way to
-								   differentiate the two (because DTFJ doesn't), although you may 
-								   be able to differentiate based on object fields
-								   (e.g. a "closed" boolean field, etc.).</p>
+								   garbage collected. Those objects where finalization has been
+								   completed are not now shown by this query, but might be in
+								   the unreachable objects histogram if they are waiting to be
+								   reclaimed.
+								   <p class="p">States</p>
+
+								   <ol class="ol">
+								   <li class="li">Initially objects with a finalize method start in the
+								   <a class="xref" href="../concepts/gcroots.html">UNFINALIZED</a> state.
+								   With a J9 heap dump this is visible as an inbound <samp class="ph codeph">&lt;Unfinalized&gt;</samp> reference from
+								   the <samp class="ph codeph">java.lang.Runtime</samp> object.</li>
+
+								   <li class="li">Once the object is no longer <a class="xref" href="../concepts/reachability.html">reachable</a>
+								   it goes onto the finalizer queue, visible via this query and with
+								   J9 heap dumps as a <a class="xref" href="../concepts/gcroots.html">FINALIZABLE GC root</a>.</li>
+
+								   <li class="li">Once finalization is completed the object is not seen in either of these two
+								   ways. Normally it would be garbage collected, but if the <samp class="ph codeph">finalizer()</samp>
+								   code made the object reachable again by storing a reference to it in an ordinary object
+								   it might still be strongly reachable.</li>
+
+								   </ol>
+
+								</div>
 
 								<p class="p"> Additionally a class-level summary of the objects is provided</p>
 
@@ -115,7 +137,7 @@
 								<p class="p">
 									This query shows the thread locals of the daemon thread that
 									performs the object finalizations. If there are any, this
-									indicates miss-use in at least one of the processed finalizers
+									indicates misuse in at least one of the processed finalizers
 									(
 									<span class="keyword cmdname">finalize()</span>
 									implemented wrong) and might cause severe problems (e.g.
@@ -133,6 +155,10 @@
 				</table>
 </div>
 
+			<p class="p">The <a class="xref" href="../reference/inspections/component_report.html#ref_inspections_component_report__finalizer">component report</a> also includes information about objects
+			belonging to the component which implement the <samp class="ph codeph">finalize()</samp> method
+			and so could be finalized at some point.</p>
+
 		</div>
 
 	</div>