Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-7008595

Class loader leak caused by keepAliveTimer thread in KeepAliveCache

    Details

    • Subcomponent:
    • Resolved In Build:
      b130
    • CPU:
      generic
    • OS:
      generic
    • Verification:
      Verified

      Backports

        Description

        SYNOPSIS
        --------
        Class loader leak caused by keepAliveTimer thread in KeepAliveCache

        OPERATING SYSTEM
        ----------------
        All

        FULL JDK VERSION
        ----------------
        All (1.4.2, 5.0, 6 and 7)

        PROBLEM DESCRIPTION from LICENSEE
        ---------------------------------
        The "keepAliveTimer" daemon thread created in sun.net.www.http.KeepAliveCache inherits the context class loader of the parent thread, and therefore holds a reference to that class loader.

        This is fine if the thread's context class loader is the system class loader, but it's bad if the context class loader is a custom class loader that may need to be unloaded at some future point (e.g. in the context of an appserver). The reference held by this daemon thread means that the class loader can never become eligible for GC.

        SUGGESTED FIX from LICENSEE
        ---------------------------
        The solution is to set the daemon thread's context class loader to null when it is created (in KeepAliveCache.put()). A similar solution is already used in java.io.StreamCloser.addToQueue().

        WORKAROUND
        ----------
        None

        REPRODUCTION INSTRUCTIONS
        -------------------------
        1. javac KeepAliveCacheTest.java
        2. java -XX:+HeapDumpOnCtrlBreak KeepAliveCacheTest

        Take a heapdump when prompted. Open the heapdump (e.g. in MAT) and look for a byte array of 20MB owned by the leaked class loader. It can easily be seen that the class loader is being retained by the "keepAliveTimer" thread.

        TESTCASE
        --------
        import java.lang.reflect.*;
        import java.net.*;
        import java.io.*;

        public class KeepAliveCacheTest {
            public static void main (String args[]) throws Exception {
                MyClassLoader myCL = new MyClassLoader();

                Thread.currentThread().setContextClassLoader(myCL);

                Class appClass = myCL.loadClass("ApplicationClass", new File("ApplicationClass.class"));
                Method createCacheMethod = appClass.getDeclaredMethod("getConnection", (Class[])null);
                createCacheMethod.setAccessible(true);
                createCacheMethod.invoke(null);
               
                // Destroy all our references to the application Class and its Class loader
                Thread.currentThread().setContextClassLoader(null);
                myCL = null;
                createCacheMethod = null;
                appClass = null;

                // The application Class and its Class loader should now be eligible for GC
                // Let's try to clear them away...
                System.gc();

                System.out.println("Finished. Take heapdump now");
                Thread.sleep(Long.MAX_VALUE);
            }
        }

        class MyClassLoader extends ClassLoader {
            public Class loadClass(String className, File classFile) throws Exception {
                FileInputStream is = new FileInputStream(classFile);

                byte[] classBytes = new byte[(int)classFile.length()];
                int data;
                int count = 0;
                while ((data = is.read()) != -1) {
                    classBytes[count] = (byte)data;
                    count++;
                }

                return defineClass(className, classBytes, 0, classBytes.length);
            }
        }

        class ApplicationClass {
            static byte[] bytes;

            public static void getConnection() throws Exception {
                // create 20MB byte array to highlight the leak
                bytes = new byte[20971520];
               
                URL url = new URL("http://www.google.com");

                // Open a URLConnection, read the InputStream and close it
                // This should ensure that a KeepAliveCache is created
                InputStream is = url.openConnection().getInputStream();
                while (is.read() != -1) {}
                is.close();
            }
        }

          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  chegar Chris Hegarty
                  Reporter:
                  dkorbel David Korbel (Inactive)
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  1 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved:
                    Imported:
                    Indexed: