我正在玩
Java中的classLoaders并注意到一件奇怪的事情.如果classLoader从jar加载一个类,即使你没有引用你的classLoader,这个jar也会无限期地被锁定.
在下面的示例中,jar包含一个名为HelloWorld的类.我所做的是尝试通过classLoader加载jar中包含的类,该类动态地添加jar.如果将skip设置为true并且不调用Class.forName,则可以删除jar,但如果不跳过,即使不引用classLoader(classLoader = null),也不能删除jar,直到JVM退出.
这是为什么?
PS:我使用的是java 6,代码非常冗长,用于测试目的
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,len);
}
finput.close();
foutput.close();
return true;
}
}
public boolean deleteJar() {
File destJar = new File("C:\\Users\\Adel\\Desktop\\classes.jar");
return destJar.delete();
}
}
解决方法
我找到了答案和解决方法.
基于这个article和这个惊人的相关article,使用Class.forName(className,classLoader)是一个坏习惯,因为它使类无限期地缓存在内存中.
解决方案是使用classLoader.loadClass(clasName),然后完成后,取消引用classLoader并使用以下命令调用垃圾收集器:
classLoader = null; System.gc();
希望这有助于他人!
总结
以上是DEVMAX为你收集整理的Java classLoader困境与锁定的罐子全部内容。
如果觉得DEVMAX网站内容还不错,欢迎将DEVMAX网站推荐给好友。