Recently I was analyzing the heap dumps and learnt about how classloader leak occurs.

Let's see this code example 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);}@Overrideprotected void finalize() {System.out.println("*** CustomClassLoader finalized!");}}Foo.java----------------------------------public class Foo {public Foo() {System.out.println("Test ClassLoader: " + this.getClass().getClassLoader());}@Overrideprotected void finalize() {System.out.println( this + " finalized!");   }}

The output of this is 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, let's 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!