javamemoryclassloaderclassloading

Why isn't my class unloaded when I erase any link to them and to ClassLoader?


I am trying to learn how class unloading works in Java. I have created a test application that just loads a class, and than waits.

package com.expirement;

import java.net.URL;
import java.net.URLClassLoader;

public class Main {
    public static void main(String[]args) throws Exception {
        f();
        try{
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }
        //breakpoint
    }
    public static void f() throws Exception {
        URLClassLoader cl=new URLClassLoader(new URL[]{
            Main.class.getProtectionDomain().getCodeSource().getLocation()
        });
        Class<?> c = cl.loadClass ("com.expirement.Loadable1");
        cl.close();
        System.gc();
        cl = null;
        c = null;
    }
}

Unfortunately, on that breakpoint the JVM hasn't unloaded the class.

My questions are:


Solution

  • When you do not specify a parent loader, the URLClassLoader will use the application class loader and since you are using a URL from the classpath, the requested class can be resolved through this parent loader.

    You can specify the bootstrap loader (represented as null) as the parent loader.

    Note further that the chances of the objects getting collected are higher when you set the variables to null or leave the method containing them before you trigger the garbage collection.

    For example:

    public class Main {
        public static void main(String[] args) throws Exception {
            ReferenceQueue<Class<?>> queue = new ReferenceQueue<>();
            WeakReference<Class<?>> ref = f(queue);
            do System.gc(); while(queue.remove(1000) != ref);
            System.out.println("class has been collected");
        }
    
        public static
            WeakReference<Class<?>> f(ReferenceQueue<Class<?>>queue) throws Exception {
    
            URL url = Main.class.getProtectionDomain().getCodeSource().getLocation();
            try(URLClassLoader cl=new URLClassLoader(new URL[]{ url }, null)) {
               Class<?> c = cl.loadClass("com.expirement.Loadable1");
               return new WeakReference<>(c, queue);
            }
        }
    }
    

    But always keep in mind that this is not guaranteed to collect the class. System.gc() is just a hint that might get ignored by the JVM, but even when the garbage collector runs, it does not guaranty that all objects get collected, and finally, class unloading is an optional feature, which is not supported by every JVM and can be turned off in some implementations.