[jboss-cvs] JBossAS SVN: r101207 - trunk/testsuite/src/main/org/jboss/test/classloader/leak/clstore.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Sun Feb 21 11:25:55 EST 2010


Author: bstansberry at jboss.com
Date: 2010-02-21 11:25:55 -0500 (Sun, 21 Feb 2010)
New Revision: 101207

Modified:
   trunk/testsuite/src/main/org/jboss/test/classloader/leak/clstore/ClassLoaderStore.java
   trunk/testsuite/src/main/org/jboss/test/classloader/leak/clstore/LeakAnalyzer.java
Log:
Improve handling of static field refs

Modified: trunk/testsuite/src/main/org/jboss/test/classloader/leak/clstore/ClassLoaderStore.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/classloader/leak/clstore/ClassLoaderStore.java	2010-02-21 03:51:33 UTC (rev 101206)
+++ trunk/testsuite/src/main/org/jboss/test/classloader/leak/clstore/ClassLoaderStore.java	2010-02-21 16:25:55 UTC (rev 101207)
@@ -46,6 +46,8 @@
    private final Map<String, WeakReference<ClassLoader>> classloaders = new HashMap<String, WeakReference<ClassLoader>>();
 
    private final int depth = Integer.parseInt(System.getProperty("jboss.classloader.leak.test.depth", "12"));
+   
+   private final boolean verbose = Boolean.getBoolean("jboss.classloader.leak.test.verbose");
 
    private ClassLoaderStore()
    {
@@ -156,7 +158,7 @@
          
          Thread.sleep(20);
          
-         String report = leakAnalyzer.exploreObjectReferences(datapoints, weakReferenceOnLoader.get(), this.depth, true, false);
+         String report = leakAnalyzer.exploreObjectReferences(datapoints, weakReferenceOnLoader.get(), this.depth, true, !verbose);
          log.info(report);
          if (reportHTMLFile != null)
          {

Modified: trunk/testsuite/src/main/org/jboss/test/classloader/leak/clstore/LeakAnalyzer.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/classloader/leak/clstore/LeakAnalyzer.java	2010-02-21 03:51:33 UTC (rev 101206)
+++ trunk/testsuite/src/main/org/jboss/test/classloader/leak/clstore/LeakAnalyzer.java	2010-02-21 16:25:55 UTC (rev 101207)
@@ -29,7 +29,6 @@
 import java.lang.reflect.Field;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -50,6 +49,8 @@
 {
    private static final Logger log = Logger.getLogger(LeakAnalyzer.class);
 
+   private static final String SEP = System.getProperty("line.separator");
+   private Object root;
    /**
     * Create a new LeakAnalyzer.
     * 
@@ -83,35 +84,36 @@
    public String exploreObjectReferences(HashMap<Long, List<ReferenceDataPoint>> referencesMap, Object thatObject, int maxLevel, boolean useToString,
          boolean condensed)
    {
+      this.root = thatObject;
+      
       ReferenceReportNode root = new ReferenceReportNode(callToString(thatObject, useToString));
 
       Set<ReferenceReportNode> prunableLeaves = new HashSet<ReferenceReportNode>();
 
-      CharArrayWriter charArray = new CharArrayWriter();
-      PrintWriter out = new PrintWriter(charArray);
+      StringBuilder sb = new StringBuilder();
 
       try
       {
          exploreObject(root, thatObject, 0, maxLevel, useToString, false, referencesMap, new HashSet<String>(), prunableLeaves);
 
-         for (Iterator<ReferenceReportNode> it = prunableLeaves.iterator(); it.hasNext();)
+         for (ReferenceReportNode nonCrit : prunableLeaves)
          {
-            ReferenceReportNode nonCrit = it.next();
             nonCrit.markNonCritical();
             if (condensed)
                nonCrit.removeBranch();
          }
 
-         writeReport(root, 0, out);
+         writeReport(root, 0, condensed, sb);
       }
       catch (Exception e)
       {
-         charArray = new CharArrayWriter();
-         out = new PrintWriter(charArray);
+         CharArrayWriter charArray = new CharArrayWriter();
+         PrintWriter out = new PrintWriter(charArray);
          e.printStackTrace(out);
+         sb = new StringBuilder(charArray.toString());
       }
 
-      return charArray.toString();
+      return sb.toString();
    }
    
    
@@ -146,109 +148,69 @@
 
       alreadyExplored.add(index);
 
-      log.info("resolving references of " + callToString(source, useToString) + "...");
+//      log.info("resolving references of " + callToString(source, useToString) + "...");
+      log.info(currentLevel + " -- resolving references of " + source.getClass());
       Long sourceTag = new Long(this.getTagOnObject(source));
       List<ReferenceDataPoint> listPoints = mapDataPoints.get(sourceTag);
       if (listPoints == null)
       {
-         log.info("didn't find references");
+         log.debug("didn't find references");
          return;
       }
 
-      log.info("References found");
+      log.debug("References found");
 
       for (ReferenceDataPoint point : listPoints)
       {         
          ReferenceReportNode child = new ReferenceReportNode();
          
-         Object nextReference = treatReference(child, point, useToString);
-
-         if (nextReference != null && !weakAndSoft)
-         {
-            if (nextReference instanceof Reference<?>)
-            {
-               // WeakHashMap$Entry and ThreadLocal$ThreadLocalMap$Entry are
-               // special cases, where the Entry key is a weak ref, but the 
-               // value is strong. We don't want to ignore similar cases. So
-               // only mark as prunable if the ref is the standard
-               // java.lang.ref.Referent.referent -- all others are potential
-               // strong references
-               // We also don't stop at SoftReferences, since if they survive
-               // our attempts at flushing them out, we want to know about them
-               String msg = child.getMessage();
-               if (msg.indexOf("FieldReference private java.lang.Object java.lang.ref.Reference.referent=") >= 0
-                     && !(nextReference instanceof SoftReference<?>))
-               {                  
-                  if (nextReference instanceof Map.Entry<?,?>)
-                  {
-                     // WeakHashMap$Entry is suspicious. 
-                     // Put in some more info about the entry
-                     @SuppressWarnings("unchecked")
-                     Map.Entry entry = (Entry) nextReference;
-                     Object key = entry.getKey();
-                     msg += " KEY=" + (key == null ? " null" : key.getClass().getName() + "@" + System.identityHashCode(key));
-                     Object val= entry.getValue();
-                     msg += " VALUE=" + (val == null ? " null" : val.getClass().getName() + "@" + System.identityHashCode(val));
-                     child.setMessage(msg);
-                  }
-                  
-                  prunableLeaves.add(child);                  
-                  nextReference = null;
-               }
-               else if (msg.indexOf("java.lang.ThreadLocal$ThreadLocalMap$Entry") >= 0)
-               {
-                  // Get the key and follow that to see why it isn't released
-                  nextReference = ((Reference<?>) nextReference).get();
-               }
-               // else just keep going
-               
-               
-            }
-         }
-
+         Object nextReference = treatReference(child, point, useToString, weakAndSoft, prunableLeaves);
+         
          if (nextReference != null)
          {
             exploreObject(child, nextReference, currentLevel + 1, maxLevel, useToString, weakAndSoft, mapDataPoints,
                   alreadyExplored, prunableLeaves);
-         }
+         } 
          
          if (child.getMessage() != null || child.getChildren().size() > 0)
-            node.addChild(child);
-            
+            node.addChild(child);            
       }
 
    }
 
-   private void writeReport(ReferenceReportNode node, int level, PrintWriter out)
+   private static void writeReport(ReferenceReportNode node, int level, boolean condensed, StringBuilder sb)
    {
-      out.print("<br>");
-      out.print(writeLevel(level));
-      if (node.isCritical())
+      sb.append("<br>");
+      writeLevel(level, sb);
+      if (condensed || node.isCritical())
       {
-         out.print("<b>");
-         if (node.isLeaf())
+         sb.append("<b>");
+         boolean leaf = node.isLeaf();
+         if (leaf)
          {
-            out.print("<font color=\"red\">");
+            sb.append("<font color=\"red\">");
          }         
-         out.print(node.getMessage());  
-         if (node.isLeaf())
+         sb.append(node.getMessage());  
+         if (leaf)
          {
-            out.print("</font>");
+            sb.append("</font>");
          }
-         out.println("</b>");
+         sb.append("</b>");
       }
       else
       {
-         out.println(node.getMessage());
+         sb.append(node.getMessage());
       }
+      
+      sb.append(SEP);
 
-      for (Iterator<ReferenceReportNode> it = node.getChildren().iterator(); it.hasNext();)
+      for (ReferenceReportNode child : node.getChildren())
       {
-         writeReport(it.next(), level + 1, out);
+         writeReport(child, level + 1, condensed, sb);
       }
    }
 
-   private String callToString(Object obj, boolean callToString)
+   private static String callToString(Object obj, boolean callToString)
    {
       try
       {
@@ -274,7 +236,7 @@
       }
    }
 
-   private Object treatReference(ReferenceReportNode node, ReferenceDataPoint point, boolean useToString)
+   private Object treatReference(ReferenceReportNode node, ReferenceDataPoint point, boolean useToString, boolean weakAndSoft, Set<ReferenceReportNode> prunableLeaves)
    {
       Object referenceHolder = null;
       if (point.getReferenceHolder() == 0 || point.getReferenceHolder() == -1)
@@ -287,15 +249,14 @@
       }
 
       Object nextReference = null;
-      CharArrayWriter charArray = new CharArrayWriter();
-      PrintWriter out = new PrintWriter(charArray);
+      StringBuilder sb = new StringBuilder();
 
       switch (point.getReferenceType())
       {
          case JVMTICallBack.JVMTI_REFERENCE_CLASS :
             // Reference from an object to its class.
-            out.print("InstanceOfReference:");
-            out.println("ToString=" + callToString(referenceHolder, useToString));
+            sb.append("InstanceOfReference: ");
+            sb.append(callToString(referenceHolder, useToString));
 
             nextReference = referenceHolder;
             break;
@@ -336,7 +297,7 @@
                   fieldName = field.toString();
                }
             }
-            out.print("FieldReference " + fieldName + "=" + callToString(referenceHolder, useToString));
+            sb.append("FieldReference ").append(fieldName).append("=").append(callToString(referenceHolder, useToString));
             nextReference = referenceHolder;
             break;
          }
@@ -347,33 +308,33 @@
             
             if (referenceHolder == null)
             {
-               out.println("arrayRef Position " + point.getIndex() + " is gone");
+               sb.append("arrayRef Position " + point.getIndex() + " is gone");
             }
             else
             {
-               out.println("arrayRef " + referenceHolder.getClass().getName() + "[" + point.getIndex()
+               sb.append("arrayRef " + referenceHolder.getClass().getName() + "[" + point.getIndex()
                      + "] id=@" + System.identityHashCode(referenceHolder));
             }
             nextReference = referenceHolder;
             break;
          case JVMTICallBack.JVMTI_REFERENCE_CLASS_LOADER :
             // Reference from a class to its class loader.            
-            out.println("ClassLoaderReference @ " + callToString(referenceHolder, useToString));
+            sb.append("ClassLoaderReference @ " + callToString(referenceHolder, useToString));
             nextReference = referenceHolder;
             break;
          case JVMTICallBack.JVMTI_REFERENCE_SIGNERS :
             // Reference from a class to its signers array.
-            out.println("ReferenceSigner@" + callToString(referenceHolder, useToString));
+            sb.append("ReferenceSigner@" + callToString(referenceHolder, useToString));
             nextReference = referenceHolder;
             break;
          case JVMTICallBack.JVMTI_REFERENCE_PROTECTION_DOMAIN :
             // Reference from a class to its protection domain.
-            out.println("ProtectionDomain@" + callToString(referenceHolder, useToString));
+            sb.append("ProtectionDomain@" + callToString(referenceHolder, useToString));
             nextReference = referenceHolder;
             break;
          case JVMTICallBack.JVMTI_REFERENCE_INTERFACE :
             // Reference from a class to one of its interfaces.
-            out.println("ReferenceInterface@" + callToString(referenceHolder, useToString));
+            sb.append("ReferenceInterface@" + callToString(referenceHolder, useToString));
             nextReference = referenceHolder;
             break;
          case JVMTICallBack.JVMTI_REFERENCE_STATIC_FIELD :// Reference from a
@@ -412,7 +373,24 @@
             {
                fieldName = field.toString();
             }
-            out.println("StaticFieldReference " + fieldName);
+            sb.append("StaticFieldReference " + fieldName);
+            
+            // A static field reference is either a leak or a cycle, depending
+            // on whether the field's class was loaded by our root CL Check for this
+            // and cut short the analysis of this tree by simply recording
+            // its ClassLoader
+            ClassLoader cl = clazz.getClassLoader();
+            if (root.equals(cl))
+            {
+               prunableLeaves.add(node);
+            }
+            else
+            {
+               log.info("Found a static reference leak to class " + clazz);
+               ReferenceReportNode child = new ReferenceReportNode();
+               child.setMessage("ClassLoaderReference @ " + callToString(cl, useToString));
+               node.addChild(child);
+            }
             nextReference = null;
             break;
          }
@@ -423,11 +401,11 @@
             // is the index into constant pool table of the class, starting
             // at 1. See The Constant Pool in the Java Virtual Machine
             // Specification.
-            out.println(" ReferenceInterface@" + callToString(referenceHolder, useToString));
+            sb.append(" ReferenceInterface@" + callToString(referenceHolder, useToString));
             nextReference = referenceHolder;
             break;
          case JVMTICallBack.ROOT_REFERENCE :
-            out.println("Root");
+            sb.append("Root");
             nextReference = null;
             break;
          case JVMTICallBack.THREAD_REFERENCE :
@@ -450,7 +428,11 @@
                 */
 
                String methodName = this.getMethodName(point.getMethod());
-               out.println("Reference inside a method - " + className + "::" + methodName);
+               sb.append("Reference inside a method - " + className + "::" + methodName);
+               if (methodClass.isAssignableFrom(getClass()))
+               {
+                  prunableLeaves.add(node);
+               }
             }
             nextReference = null;
             break;
@@ -458,20 +440,59 @@
             log.warn("unexpected reference " + point);
       }
 
-      String msg = charArray.toString();
-      if (msg.trim().length() > 0)
-         node.setMessage(msg);
+      if (sb.length() > 0)
+         node.setMessage(sb.toString());
 
+      if (nextReference != null && !weakAndSoft)
+      {
+         if (nextReference instanceof Reference<?>)
+         {
+            // WeakHashMap$Entry and ThreadLocal$ThreadLocalMap$Entry are
+            // special cases, where the Entry key is a weak ref, but the 
+            // value is strong. We don't want to ignore similar cases. So
+            // only mark as prunable if the ref is the standard
+            // java.lang.ref.Referent.referent -- all others are potential
+            // strong references
+            // We also don't stop at SoftReferences, since if they survive
+            // our attempts at flushing them out, we want to know about them
+            String msg = node.getMessage();
+            if (msg.indexOf("FieldReference private java.lang.Object java.lang.ref.Reference.referent=") >= 0
+                  && !(nextReference instanceof SoftReference<?>))
+            {                  
+               if (nextReference instanceof Map.Entry<?,?>)
+               {
+                  // WeakHashMap$Entry is suspicious. 
+                  // Put in some more info about the entry
+                  @SuppressWarnings("unchecked")
+                  Map.Entry entry = (Entry) nextReference;
+                  Object key = entry.getKey();
+                  msg += " KEY=" + (key == null ? " null" : key.getClass().getName() + "@" + System.identityHashCode(key));
+                  Object val= entry.getValue();
+                  msg += " VALUE=" + (val == null ? " null" : val.getClass().getName() + "@" + System.identityHashCode(val));
+                  node.setMessage(msg);
+               }
+               
+               prunableLeaves.add(node);                  
+               nextReference = null;
+            }
+            else if (msg.indexOf("java.lang.ThreadLocal$ThreadLocalMap$Entry") >= 0)
+            {
+               // Get the key and follow that to see why it isn't released
+               nextReference = ((Reference<?>) nextReference).get();
+            }
+            // else just keep going   
+            
+         }
+      }
+
       return nextReference;
    }
    
-   private static String writeLevel(int level)
+   private static void writeLevel(int level, StringBuilder sb)
    {
-      StringBuffer levelSb = new StringBuffer();
       for (int i = 0; i <= level; i++)
       {
-         levelSb.append("!--");
+         sb.append("!--");
       }
-      return levelSb.toString();
    }
 }




More information about the jboss-cvs-commits mailing list