Relates :
|
|
Relates :
|
|
Relates :
|
Name: rmT116609 Date: 11/06/2003 A DESCRIPTION OF THE REQUEST : At present there is no explicit end-of-lifecycle support on the ClassLoader interface. As a result, if the ClassLoader holds open files, sockets, etc. as a performance optimisation there is no clear way of indicating that the use of the ClassLoader is complete. An example of this is the URLClassLoader - if one of the URLs is a jar: URL then as soon a class has been loaded (or a resource accessed) by the URLClassLoader the JAR file is left open until the JarFile object (allocated by sun.misc.URLClassPath) is garbage collected. On Windows 2000 this manifests itself as a file lock which prevents the file being deleted. This makes reasonable sense while the ClassLoader is in use since replacing the file at run-time could lead to strange behaviour. However, if an application can guarantee that the ClassLoader is no longer in use (or at least guarantee that it should no longer be used to load any further resources or classes) it would make sense to allow an application to dispose of the ClassLoader explicitly, closing any resources held by the ClassLoader. E.g. by adding a dispose() method to the ClassLoader class. Without an explicit mechanism for disposing of ClassLoaders there are problems associated with an application cleaning up the resources being accessed by the ClassLoader. At present there is no explicit end-of-lifecycle support on the ClassLoader interface. As a result, if the ClassLoader holds open files, sockets, etc. as a performance optimisation there is no clear way of indicating that the use of the ClassLoader is complete. Consequently, the files remain open and locked until the VM completes. An example of this is the URLClassLoader - if one of the URLs is a jar: URL then as soon a class has been loaded (or a resource accessed) by the URLClassLoader the JAR file is left open even after the JarFile object (allocated by sun.misc.URLClassPath) is garbage collected. On Windows 2000 this manifests itself as a file lock which prevents the file being deleted even from within the VM. This makes reasonable sense while the ClassLoader is in use since replacing the file at run-time could lead to strange behaviour. However, if an application can guarantee that the ClassLoader is no longer in use (or at least indicate that it should no longer be used to load any further resources or classes) it would make sense to allow an application to dispose of the ClassLoader explicitly, closing any resources held by the ClassLoader. E.g. by adding a dispose() method to the ClassLoader class. Without an explicit mechanism for disposing of ClassLoaders there are problems associated with an application cleaning up the resources being accessed by a custom ClassLoader. For example: 1) An application creates a temporary JAR file 2) The application creates a URLClassLoader to access the classes/resources contained within the JAR file 3) The application finished using the URLClassLoader and sets all references to the ClassLoader and instances of classes loaded by the ClassLoader to null. 4) The application can now *not* delete the temporary JAR file until the URLClassLoader is garbage collected. Since there is no way of forcing garbage collection in Java the application can never guarantee deletion of the files. Using File.deleteOnExit() doesn't appear to work to delete these files either. JUSTIFICATION : The current ClassLoader interface/contract implicitly forces implementations to rely on garbage collection for cleaning up any resources it holds (which doesn't always work). EXPECTED VERSUS ACTUAL BEHAVIOR : EXPECTED - ClassLoader should provide a method such as dispose() that prevents the ClassLoader from being used to access new resources/classes. Alternatively a method that indicates a ClassLoader should flush resources held until a new resource/class load request occurs would also be acceptable. ACTUAL - ClassLoaders such as URLClassLoader only free up their resources (such as open JarFile instances) on garbage collection through the normal finalization mechanism (which doesn't actually work in some cases). ---------- BEGIN SOURCE ---------- import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URL; import java.net.URLClassLoader; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ClassLoaderDemo { private final static String RESOURCE_NAME = "resource.txt"; public static void main(String[] args) throws Exception { System.out.println("Java version=[" + System.getProperty("java.version") + "]"); // Create a zip file with a file called resource.txt in it File f = createZipFile(); // Look - it gets deleted when I haven't pointed the classloader at it deleteFile(0, f); // Recreate the file as I seem to have deleted it f = createZipFile(); // Create classloader that uses the zip file URL jarUrl = f.toURL(); ClassLoader classLoader = new NoisyUrlClassLoader(new URL[] { jarUrl }); // Load resource.txt from the zip file using the classloader InputStream is = classLoader.getResourceAsStream(RESOURCE_NAME); BufferedReader br = new BufferedReader(new InputStreamReader(is)); System.out.println("Read from resource: " + br.readLine()); br.close(); // I cannot indicate I have finished with the ClassLoader! // classLoader.dispose(); // classLoader.freeResources(); // classLoader.dieDieDie(); // Do various things to try to delete the file deleteFile(1, f); // Try making the classloader eligable for GC classLoader = null; // I'm not hanging on to anything returned by the ClassLoader // here or that references things returned by the classloader is = null; br = null; // Try now... deleteFile(2, f); // Poke the garbage collector - this causes the classloader to be collected on my machine // with Java 1.3.1_09 System.gc(); // But still can't delete it deleteFile(3, f); if (f.exists()) { System.out.println("You're now the proud owner of a file called=[" + f + "]"); } // And I bet deleteOnExit doesn't work either.... } private static boolean deleteFile(int count, File f) { // Attempt to delete the Jar file boolean deletedFile = f.delete(); if (deletedFile) { System.out.println("Successfully delected file=[" + f + "], count=[" + count + "]"); } else { System.out.println("Unable to delete file=[" + f + "], count=[" + count + "]"); } return deletedFile; } private static File createZipFile() throws IOException { // Create a Jar/Zip file with a single entry File f = File.createTempFile("temp", ".jar"); // Try to make the VM delete the file on exit (it doesn't work though) f.deleteOnExit(); FileOutputStream fos = new FileOutputStream(f); ZipOutputStream zos = new ZipOutputStream(fos); ZipEntry ze = new ZipEntry(RESOURCE_NAME); zos.putNextEntry(ze); OutputStreamWriter osw = new OutputStreamWriter(zos); osw.write("Testing 1,2,3"); osw.flush(); zos.closeEntry(); osw.close(); System.out.println("Created file=[" + f + "]"); return f; } // URLClassLoader that shouts about being garbage collected private static class NoisyUrlClassLoader extends URLClassLoader { public NoisyUrlClassLoader(URL [] url) { super(url); } protected void finalize() throws Throwable { System.out.println(">>>>NoisyUrlClassloader is being collected"); super.finalize(); } } } ---------- END SOURCE ---------- (Incident Review ID: 223861) ======================================================================
|