JDK-6484556 : jhat should be able to help in finding classloader leaks
  • Type: Enhancement
  • Component: tools
  • Sub-Component: launcher
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2006-10-20
  • Updated: 2011-05-18
  • Resolved: 2011-05-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6u1Fixed 7 b03Fixed
Description
FULL PRODUCT VERSION :


ADDITIONAL OS VERSION INFORMATION :
any OS

A DESCRIPTION OF THE PROBLEM :
I'm filing this ticket after discussion with Sundararajan.A.

This is not a bug per se; classification as RFE is ok too.

A small change in jhat makes it possible to use jhat to diagnose classloader leaks. See http://blogs.sun.com/fkieviet/entry/classloader_leaks_the_dreaded_java and
http://blogs.sun.com/fkieviet/entry/how_to_fix_the_dreaded

The change is in Snapshot.java in the method rootsetReferencesTo(). I've tried to change as few lines of code as possible.

The change is as follows: when the user asks for references to be traced from a classloader object, it now includes all references from classes to the classloader, and from the instances of these classes to the classloader.

Similarly, when the user asks for references to be traced from a class object, it now includes all references from the instances of these classes.

The changed method is pasted below: the changes are between the markers FGK and FGK.


    public ReferenceChain[] rootsetReferencesTo(JavaHeapObject target, boolean includeWeak) {
        Vector fifo = new Vector(); // This is slow... A real fifo would help
        // Must be a fifo to go breadth-first
        Hashtable visited = new Hashtable();
        // Objects are added here right after being added to fifo.
        Vector result = new Vector();
        visited.put(target, target);
        fifo.addElement(new ReferenceChain(target, null));

        while (fifo.size() > 0) {
            ReferenceChain chain = (ReferenceChain) fifo.elementAt(0);
            fifo.removeElementAt(0);
            JavaHeapObject curr = chain.getObj();
            if (curr.getRoot() != null) {
                result.addElement(chain);
                // Even though curr is in the rootset, we want to explore its
                // referers, because they might be more interesting.
            }
            Enumeration referers = curr.getReferers();
            while (referers.hasMoreElements()) {
                JavaHeapObject t = (JavaHeapObject) referers.nextElement();
                if (t != null && !visited.containsKey(t)) {
                    if (includeWeak || !t.refersOnlyWeaklyTo(this, curr)) {
                        visited.put(t, t);
                        fifo.addElement(new ReferenceChain(t, chain));
                    }
                }
            }
            
            // FGK Special processing when a class is queried:
            // Include all instances of the sought after class
            if (target == curr && curr instanceof JavaClass) {
                JavaClass clazz = (JavaClass) curr;
                referers = clazz.getInstances(false);
                
                // copy & paste from above: go over all objects
                while (referers.hasMoreElements()) {
                    JavaHeapObject t = (JavaHeapObject) referers.nextElement();
                    if (t != null && !visited.containsKey(t)) {
                        if (includeWeak || !t.refersOnlyWeaklyTo(this, curr)) {
                            visited.put(t, t);
                            fifo.addElement(new ReferenceChain(t, chain));
                        }
                    }
                }
            }
            // FGK.
            
            // FGK Special processing when a classloader is queried:
            // Include all instances of classes loaded by this classloader as references
            if (target == curr
                && target instanceof JavaObject
                && findClass("java.lang.ClassLoader").isAssignableFrom(target.getClazz())) {
                
                // Find all classes that have this as classloader; for each class add
                // references for all the class's instances to the classloader
                for (Iterator iter = classes.values().iterator(); iter.hasNext();) {
                    JavaClass cand = (JavaClass) iter.next();
                    if (cand.getLoader() == target) {
                        referers = cand.getInstances(false);
                        
                        // copy & paste from above: go over all objects
                        while (referers.hasMoreElements()) {
                            JavaHeapObject t = (JavaHeapObject) referers.nextElement();
                            if (t != null && !visited.containsKey(t)) {
                                if (includeWeak || !t.refersOnlyWeaklyTo(this, curr)) {
                                    visited.put(t, t);
                                    fifo.addElement(new ReferenceChain(t, chain));
                                }
                            }
                        }
                    }
                }
            }
            // FGK.
        }



REPRODUCIBILITY :
This bug can be reproduced always.

Comments
EVALUATION The following changes may be made. * In JavaHeapObject.visitReferencedObjects() method, from every object (JavaHeapObject) visit it's JavaClass. This is correct because a class is reachable from instance(s) of it. * Also, from every JavaClass object, visit it's superclass, signers, protection domain and it's loader (in addition to the static reference fields of the class). This is also correct because loader is reachable from JavaClass. [reverse references - loader-to-it's-classes-references is kept in a Vector field of java.lang.ClassLoader class] This change covers the perm. gen leak example cited in description.
26-10-2006

EVALUATION I agree with ClassLoader to loaded classes references. I am not sure of Class to it's instances. If we need that, do we have to consider subclass instances as well? How about interface implementors. In any case, since we have to fix to include references from ClassLoader to loaded (i.e., defined) classes, I accept this bug.
23-10-2006