[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