eran levi
eran levi

Reputation: 133

Unloading a classloader

my final goal is to be able to reload classes after they have been already loaded to the JVM.

After reading the following answer Unloading classes in java? , I have been trying to implement my own Class-Loader, which itself creates a different instance of Class-Loader(same type of it's own) for each class it loads.

so, the outcome is one class per one Class-Loader.

The purpose is to be able to GC the class, meaning all of its instances, then to unload its class-loader , and to be able to reload the same class from its bytes.

the problem is - I can see my class instance being garbage collected using finalize() method, but I cant get my Class-Loader to unload, or to be garbage collected .
is there any code example, a simple test, that shows how it can be done?

thanks, any help would be appreciated

Edit:

to be clearer , i am interested in code examples where the instantiation of the new objects is via the 'new( )' operand, and the Class-loader isn't explicitly reloading the Class in the main, but after the next 'new( )' is called.

Upvotes: 7

Views: 8094

Answers (2)

Jarek Przygódzki
Jarek Przygódzki

Reputation: 4412

In IBM J9 VM things are different as classloader unloading only occurs during a global gc. This can lead to huge pause times in a global gc and to an out of memory when there are a large number of classloaders that are being created. I've encountered this problems with JMXMP where one instance of com.sun.jmx.remote.opt.util.OrderClassLoaders classloader is created for each remote message of type MBeanServerRequestMessage.CREATE_MBEAN_LOADER_PARAMS.

Upvotes: 1

dcernahoschi
dcernahoschi

Reputation: 15250

The class loaders should be garbage collected if there are no more references to them. I took this code from @PeterLawrey (thanks) (it does the same thing as yours), put a log in the custom class loader finalize() method and voila, the class loaders are garbage collected after their loaded class is gc:

 /* 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");
    }
}

Upvotes: 10

Related Questions