URLClassLoader会“挂住”所有它已经打开了的在classpath上的文件

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.JarFile;

public class TestClassLoader {

	public static void main(String[] args) throws Exception {
		File jar = new File("d:\\test\\commons-lang-2.2.jar");
		URL[] urls = new URL[]{jar.toURI().toURL()};
		URLClassLoader loader = new URLClassLoader(urls);
		Class<?> cls = loader.loadClass("org.apache.commons.lang.StringUtils");
		System.out.println(cls.getName());
		
		// 查找URLClassLoader中的ucp
		Object ucpObj = null;
		Field ucpField = URLClassLoader.class.getDeclaredField("ucp");
		ucpField.setAccessible(true);
		ucpObj = ucpField.get(loader);
		URL[] list = loader.getURLs();
		for(int i=0;i<list.length;i++){
			// 获得ucp内部的jarLoader
			Method m = ucpObj.getClass().getDeclaredMethod("getLoader", int.class);
			m.setAccessible(true);
			Object jarLoader = m.invoke(ucpObj, i);
			String clsName = jarLoader.getClass().getName();
			if(clsName.indexOf("JarLoader")!=-1){
				m = jarLoader.getClass().getDeclaredMethod("ensureOpen");
				m.setAccessible(true);
				m.invoke(jarLoader);
				m = jarLoader.getClass().getDeclaredMethod("getJarFile");
				m.setAccessible(true);
				JarFile jf = (JarFile)m.invoke(jarLoader);
				// 释放jarLoader中的jar文件
				jf.close();
				System.out.println("release jar: "+jf.getName());
			}
		}
	}
}

 

Sun的JDK里最重要的两种ClassLoader,sun.misc.Launcher.ExtClassLoader和sun.misc.Launcher.AppClassLoader都是继承了URLClassLoader的(bootstrap ClassLoader并不是Java程序可见的ClassLoader,不算在内;它甚至不继承ClassLoader这个基类,根本不是一个Java object)。别的一些自定义ClassLoader为了实现方便也会继承URLClassLoader,例如GroovyClassLoader

但就是这么重要的URLClassLoader却一直有一个特性:它一旦打开了它所知的classpath上的文件就会把那些文件全部锁住,直到它被卸载前都不会释放掉。但是ClassLoader什么时候才被释放这就无法预测了,里面引用的JarFile的finalizer什么时候被调用又得看RP,诶……

在Java 7当中URLClassLoader新实现了Closeable接口,添加了一个close()方法,专门用于处理这个问题。在调用了某个URLClassLoader实例上的close()方法后,该实例就无法再用于加载类或资源;原本已经打开的类或资源仍然是可用的。

 

 

你可能感兴趣的:(ClassLoader)