Search code examples
javamemory-managementgarbage-collection

How classloader leak is occured


I am sorry to ask you this basic question but I am not able to understand the concept. I read many SO post but I could not understand. Could you please give me code example to understand.

  1. As said in this post

Static variables cannot be elected for garbage collection while the class is loaded. They can be collected when the respective class loader (that was responsible for loading this class) is itself collected for garbage.

I understand as per theory that Classloader cannot be collected if it has a reference but I do not understand how it is possible practically. Could you please kindly explain with a code example?

Many thanks for your help!


Solution

  • Lets see this code to understand how classloader leaks possible

    Main.java

        public class Main {
        public static void main(String...args) throws Exception {
         List<Object> list = new ArrayList<>();
        loadClass(list);
        while (true) {
            System.gc();
            Thread.sleep(1000);
          }
       }
    
       private static void loadClass(List list) throws Exception {
        URL url = Main.class.getProtectionDomain().getCodeSource().getLocation();
        MyCustomClassLoader cl = new MyCustomClassLoader(url);
        Class<?> clazz = cl.loadClass("com.test.Foo");
        list.add(clazz.newInstance());
        cl = null;
          }
        }
    
       class MyCustomClassLoader extends URLClassLoader {
         public MyCustomClassLoader(URL... urls) {
         super(urls, null);
         }
        @Override
         protected void finalize() {
        System.out.println("*** CustomClassLoader finalized!");
         }
        }
    

    Foo.java

        public class Foo {
          public Foo() {
          System.out.println("Test ClassLoader: " + this.getClass().getClassLoader());
           }
         @Override
         protected void finalize() {
           System.out.println( this + " finalized!");
            }
        }
    

    The output of this as follows:

    Test ClassLoader: com.test.MyCustomClassLoader@71dac704

    So, here we can see "*** CustomClassLoader finalized!" is not called and this is because MyCustomClassLoader is holding a reference of object list as the instances loaded by classloader are kept in it.

    Now, lets change the code a bit, so here we will set list to null

     public static void main(String...args) throws Exception {
         List<Object> list = new ArrayList<>();
        loadClass(list);
        while (true) {
            System.gc();
            Thread.sleep(1000);
            list = null;
        }
      }
    

    And now see the output

    Test ClassLoader: com.test.MyCustomClassLoader@71dac704 com.test.Foo@650de12 finalized! *** CustomClassLoader finalized!