卸载类加载器

7
我的最终目标是在JVM加载类后能够重新加载这些类。
阅读了以下答案Unloading classes in java?后,我一直在尝试实现自己的Class-Loader,它本身为每个加载的类创建一个不同的Class-Loader实例(与其自身相同类型的实例)。
因此,结果是每个Class-Loader对应一个类。
目的是能够进行垃圾回收,即所有实例,然后卸载其类加载器,并能够从其字节重新加载相同的类。
问题是-我可以看到使用finalize()方法垃圾收集我的类实例,但我无法使我的Class-Loader卸载或垃圾收集。
是否有任何代码示例,简单的测试,展示如何完成此操作?
谢谢,任何帮助将不胜感激。
编辑:
为了更清晰,我对通过“new()”操作数实例化新对象的代码示例感兴趣,而类加载器在主函数中没有显式重新加载类,但在调用下一个“new()”后会重新加载。

当您通过自定义的ClassLoader加载类时,您将获得一个Class对象。无法使用new运算符。使用new意味着您的代码已链接到由new使用的类,因此,在包含new表达式的代码仍然活动时,该类无法被垃圾回收。在这一点上,您必须使用反射,例如在Class上调用newInstance()。当然,动态加载的代码本身可能会使用new来实例化其自己范围内的类。 - Holger
2个回答

10
如果没有对类加载器的引用,那么它们应该被垃圾回收。我从 @PeterLawrey(谢谢)那里获取了 这个代码 (它与你的做的事情一样),在自定义类加载器的 finalize() 方法中加入了日志记录,然后再见,当已加载的类被垃圾回收后,类加载器也会被垃圾回收。
 /* Copyright (c) 2011.  Peter Lawrey
 *
 * "THE BEER-WARE LICENSE" (Revision 128)
 * As long as you retain this notice you can do whatever you want with this stuff.
 * If we meet some day, and you think this stuff is worth it, you can buy me a beer in return
 * There is no warranty.
 */


import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;

public class LoadAndUnloadMain {
    public static void main(String... args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InterruptedException {
        URL url = LoadAndUnloadMain.class.getProtectionDomain().getCodeSource().getLocation();
        final String className = LoadAndUnloadMain.class.getPackage().getName() + ".UtilityClass";
        {
            ClassLoader cl;
            Class clazz;

            for (int i = 0; i < 2; i++) {
                cl = new CustomClassLoader(url);
                clazz = cl.loadClass(className);
                loadClass(clazz);

                cl = new CustomClassLoader(url);
                clazz = cl.loadClass(className);
                loadClass(clazz);
                triggerGC();
            }
        }
        triggerGC();
    }

    private static void triggerGC() throws InterruptedException {
        System.out.println("\n-- Starting GC");
        System.gc();
        Thread.sleep(100);
        System.out.println("-- End of GC\n");
    }

    private static void loadClass(Class clazz) throws NoSuchFieldException, IllegalAccessException {
        final Field id = clazz.getDeclaredField("ID");
        id.setAccessible(true);
        id.get(null);
    }

    private static class CustomClassLoader extends URLClassLoader {
        public CustomClassLoader(URL url) {
            super(new URL[]{url}, null);
        }

        @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            try {
                return super.loadClass(name, resolve);
            } catch (ClassNotFoundException e) {
                return Class.forName(name, resolve, LoadAndUnloadMain.class.getClassLoader());
            }
        }

        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println(this.toString() + " - CL Finalized.");
        }
    }
}

class UtilityClass {
    static final String ID = Integer.toHexString(System.identityHashCode(UtilityClass.class));
    private static final Object FINAL = new Object() {
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            System.out.println(ID + " Finalized.");
        }
    };

    static {
        System.out.println(ID + " Initialising");
    }
}

谢谢,我更感兴趣的是通过“new()”操作符实例化类的代码示例,抱歉我的问题没有表述清楚,我会进行编辑。 - eran levi

1
在IBM J9 VM中,类加载器的卸载只会在全局垃圾回收期间发生。这可能会导致全局垃圾回收的暂停时间很长,并且在创建大量类加载器时会出现内存不足的情况。我在使用JMXMP时遇到了这个问题,其中每个类型为MBeanServerRequestMessage.CREATE_MBEAN_LOADER_PARAMS的远程消息都会创建一个com.sun.jmx.remote.opt.util.OrderClassLoaders类加载器的实例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接