当我删除指向它们和 ClassLoader 的任何链接时,为什么我的类没有卸载?

问题描述 投票:0回答:1

我正在尝试了解 Java 中的类卸载是如何工作的。我创建了一个测试应用程序,它只加载一个类,然后等待。

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;
    }
}

不幸的是,在该断点处,JVM 尚未卸载该类。

我的问题是:

  • 为什么类还没有卸载?
  • 如何才能最终销毁该类(而不停止运行时)?
java memory classloader classloading
1个回答
0
投票

当您未指定父加载器时,

URLClassLoader
将使用应用程序类加载器,并且由于您使用的是类路径中的 URL,因此可以通过此父加载器解析请求的类。

您可以指定引导加载程序(表示为

null
)作为父加载程序。

进一步注意,当您将变量设置为

null
或在触发垃圾收集之前保留包含它们的方法时,对象被收集的机会会更高。

例如:

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);
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.