加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java classLoader困境与锁定的罐子

发布时间:2020-12-14 06:06:02 所属栏目:Java 来源:网络整理
导读:我正在玩 Java中的classLoaders并注意到一件奇怪的事情.如果classLoader从jar加载一个类,即使你没有引用你的classLoader,这个jar也会无限期地被锁定. 在下面的示例中,jar包含一个名为HelloWorld的类.我所做的是尝试通过classLoader加载jar中包含的类,该类动
我正在玩 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:UsersAdelDesktopclasses.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:UsersAdelDesktopFolderclasses.jar");
        File destJar = new File("C:UsersAdelDesktopclasses.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:UsersAdelDesktopclasses.jar");
        return destJar.delete();
    }

}

解决方法

我找到了答案和解决方法.

基于这个article和这个惊人的相关article,使用Class.forName(className,classLoader)是一个坏习惯,因为它使类无限期地缓存在内存中.

解决方案是使用classLoader.loadClass(clasName),然后完成后,取消引用classLoader并使用以下命令调用垃圾收集器:

classLoader = null;
System.gc();

希望这有助于他人!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读