Java dilemma classLoader with locked banks

I played with classLoaders in Java and noticed a strange thing. If classLoader loads a class from jar, this bank is blocked indefinitely, even if you do not reference classLoader.

In the example below, jar contains the HelloWorld class. I am trying to load a class contained in a jar using classLoader which dynamically adds a jar. If you set skip to true and do not call Class.forName , you can delete the jar, but if you don't skip and even if you don't reference classLoader ( classLoader = null ), the jar cannot be deleted until the JVM exits.

Why is this?

PS: I am using java 6 and the code is very verbose for testing purposes

 package loader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; public class TestClassLoader { private URLClassLoader classLoader; public TestClassLoader() throws MalformedURLException, IOException { System.out.println("Copying jar"); if (copyJar()) { System.out.println("Copying SUCCESS"); performFirstCheck(); } else { System.out.println("Copying FAILED"); } } public static void main(String[] args) throws IOException { System.out.println("Test started"); TestClassLoader testClassLoader = new TestClassLoader(); System.out.println("Bye!"); } public void performFirstCheck() throws IOException { System.out.println("Checking class HelloWorld does not exist"); if (!checkClassFound(TestClassLoader.class.getClassLoader(), false)) { System.out.println("Deleting jar"); deleteJar(); System.out.println("First Check SUCCESS"); performSecondCheck(); } else { System.out.println("First Check FAILED"); } } private void performSecondCheck() throws IOException { System.out.println("Copying jar"); if (copyJar()) { System.out.println("Copying SUCCESS"); createClassLoaderAndCheck(); } else { System.out.println("Copying FAILED"); } } private void createClassLoaderAndCheck() throws MalformedURLException { System.out.println("Creating classLoader"); createClassLoader(); System.out.println("Checking class HelloWorld exist"); if (checkClassFound(classLoader, true)) { System.out.println("Second Check SUCCESS"); classLoader = null; System.out.println("Deleting jar"); if (deleteJar()) { System.out.println("Deleting SUCCESS"); } else { System.out.println("Deleting FAILED"); } } else { System.out.println("Second Check FAILED"); } } public void createClassLoader() throws MalformedURLException { URL[] urls = new URL[1]; File classFile = new File("C:\\Users\\Adel\\Desktop\\classes.jar"); urls[0] = classFile.toURI().toURL(); classLoader = new URLClassLoader(urls); } public boolean checkClassFound(ClassLoader classLoader, boolean skip) { if (skip) { System.out.println("Skiping class loading"); return true; } else { try { Class.forName("HelloWorld", true, classLoader); return true; } catch (ClassNotFoundException e) { return false; } } } public URLClassLoader getClassLoader() { return classLoader; } public boolean copyJar() throws IOException { File sourceJar = new File("C:\\Users\\Adel\\Desktop\\Folder\\classes.jar"); File destJar = new File("C:\\Users\\Adel\\Desktop\\classes.jar"); if (destJar.exists()) { return false; } else { FileInputStream finput = new FileInputStream(sourceJar); FileOutputStream foutput = new FileOutputStream(destJar); byte[] buf = new byte[1024]; int len; while ((len = finput.read(buf)) > 0) { foutput.write(buf, 0, len); } finput.close(); foutput.close(); return true; } } public boolean deleteJar() { File destJar = new File("C:\\Users\\Adel\\Desktop\\classes.jar"); return destJar.delete(); } } 
+5
java classloader urlclassloader
source share
2 answers

I found the answer and workaround.

Based on this article and this amazing article, it is a bad habit to use Class.forName(className, true, classLoader) , because it stores cache memory indefinitely.

The solution was to use classLoader.loadClass(clasName) , and then as soon as you finish, cancel classLoader and call the garbage collector using:

 classLoader = null; System.gc(); 

Hope this helps others! :)

Background Information:

My project was complicated: we had a GWT server acting as an RMI client on another server. Therefore, to create GWT instances, it was necessary to load classes from the server and load them. GWT will later resubmit the instance to the server to store them in the database using Hibernate. To support hot deployment, we chose to dynamically load classes when the user loaded the jar and notified the server that would load classes from it and present them as available for the GWT server

+8
source share

In Java 7, URLClassLoader has a #close() method that fixes this.

+2
source share

All Articles