当前位置: 首页 > 面试题库 >

Java classLoader困境与罐子锁定

魏高邈
2023-03-14
问题内容

我在玩Java中的classLoaders时,发现一个奇怪的事情。如果classLoader从jar中加载类,则即使您未引用classLoader,此jar也将无限期锁定。

在下面的示例中,该jar包含一个名为HelloWorld的类。我要做的是尝试通过classLoader加载jar中包含的类,该类会动态添加jar。如果设置skiptrue并且不调用Class.forName,则可以删除jar,但是如果不跳过,即使取消引用classLoaderclassLoader = 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, 0, len);
            }
            finput.close();
            foutput.close();
            return true;
        }
    }

    public boolean deleteJar() {
        File destJar = new File("C:\\Users\\Adel\\Desktop\\classes.jar");
        return destJar.delete();
    }

}

问题答案:

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

基于本文和这篇惊人的相关文章,使用它是一个坏习惯,Class.forName(className, true, classLoader)因为它使类无限期地保留在内存中。

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

classLoader = null;
System.gc();

希望这对别人有帮助!:)

背景信息:

我的项目很复杂:我们有一台GWT服务器充当另一台服务器的RMI客户端。因此,要创建实例,GWT需要从服务器下载类并加载它们。稍后,GWT会将实例重新发送到服务器,以使用Hibernate将其持久化到数据库中。为了支持热部署,我们选择了动态类加载,用户可以上传一个jar,并通知服务器从中加载类并将其提供给GWT服务器。



 类似资料:
  • 嗯,这很尴尬。我想使用log4j-over-slf4j进行日志记录,因此我将jar添加到项目中,并在WebLogic应用程序中指定使用包<code>org.slf4j* 但问题是WebLogic上的commons jar库具有slf4j-log4j12.jar,这会产生冲突: 我该如何告诉 WebLogic 忽略该包?

  • 知道为什么吗 工作,但是双击罐子会给一个 错误?清单被正确放置,双击直到最近还在工作,但突然之间它给出了这个错误。我不确定我做了什么改变。 编辑: 我用来创建jar的命令是(在cmd中): Main是包文件的一部分。

  • 问题内容: 我在这里思考:如果您有2个线程执行需要同步的FAST操作,那么非阻塞方法不是比阻塞/上下文切换方法更快/更好的方法吗? 非阻塞的意思是: while(true){如果(checkAndGetTheLock())中断;} 如果您有太多线程在锁中循环,我唯一想到的就是饥饿(CPU耗尽)。 如何平衡一种方法与另一种方法? 问题答案: 以下是 Java Concurrency in Pract

  • 我有一个具有三列布局的Eclipse RCP应用程序: 编辑器区域在最右边。现在,当您获得要使用的时,编辑器区域已经添加了。这很好:我们将区域B添加到编辑器的左侧,将区域A添加到B的左侧,布局正是我们所需要的。 问题是,当您在A和B之间移动窗框时,视图A和B会改变,而不会调整编辑器区域的大小(很好;)但是当您在B和编辑器区域之间移动另一个窗框时,所有三个视图的大小都被调整;布局管理器的作用是保持A

  • null null 后端代码有一个枚举,它在代码中赋予这些预定义整数一个含义 web服务API将返回状态号 前端代码有一个类似的枚举,它在代码中赋予这些预定义整数一个含义。(如后端代码) null null 优点: 数据库定义良好并规范化 从API返回的数据是描述性的,并提供了所需的含义。 使用的状态常量已包含其含义,这减少了出错的机会。 对数据库中的列使用枚举类型有其局限性。以后使用ALTER命

  • 我正在做一个非常简单的第三方库概念验证(在本例中,是solrj)。 虽然我使用maven作为构建系统,但我得到了错误 java.lang.NoClassDefFoundError: org/apache/共用/日志/LogFactory solrj(4.10.4)没有将其定义为依赖项。 因此,我现在可以手动添加commons日志作为maven依赖项,但我不确定要添加哪一个: commons-log