admin管理员组

文章数量:1122846

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.URL;
import java.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:

  • Why has the class not been unloaded?
  • What to do so that it can finally destroy the class (without stopping the runtime)?

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:

  • Why has the class not been unloaded?
  • What to do so that it can finally destroy the class (without stopping the runtime)?
Share Improve this question edited Nov 21, 2024 at 15:08 TTT 1,8852 gold badges33 silver badges66 bronze badges asked Nov 21, 2024 at 14:58 Tech of the AbsenceTech of the Absence 478 bronze badges 4
  • 4 There is no guaranty that System.gc() does anything nor that class unloading will happen. But your chances are higher when you set the variables to null before running System.gc(). Further, check whether c.getClassLoader() == cl as you assume. I think, the default delegation parent is the application class loader and since the parent loader has a class of that name (the loaders load from the same URL), I’d expect the returned class to be loaded by the application class loader. – Holger Commented Nov 21, 2024 at 15:13
  • @Holger, thanks a lot, you have just clairified the situation a bit. I've checked, you are right about ApplicationClassLoader. Now, how can I avoid loading the class by it? – Tech of the Absence Commented Nov 21, 2024 at 15:26
  • 2 Specify a parent class loader, e.g. to use the bootstrap loader, use URLClassLoader cl=new URLClassLoader(new URL[]{ Main.class.getProtectionDomain().getCodeSource().getLocation() }, null); – Holger Commented Nov 21, 2024 at 15:28
  • @Holger, that worked. Please write this as an answer, so I can appreciate your help (upvote). – Tech of the Absence Commented Nov 21, 2024 at 15:34
Add a comment  | 

1 Answer 1

Reset to default 3

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.

本文标签: javaWhy isn39t my class unloaded when I erase any link to them and to ClassLoaderStack Overflow