Java classLoader困境与锁定的罐子 [英] Java classLoader dilemma with locked jars

查看:112
本文介绍了Java classLoader困境与锁定的罐子的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在玩Java中的classLoaders并注意到一件奇怪的事情。如果classLoader从jar加载一个类,即使你没有引用你的classLoader,这个jar也会被无限期锁定。

I was playing around with classLoaders in Java and noticed a strange thing. If a classLoader loads a class from a jar, this jar is locked indefinitely even if you unreference your classLoader.

在下面的例子中,jar包含一个名为HelloWorld的类。我所做的是尝试通过classLoader加载jar中包含的类,该类动态地添加jar。如果您设置跳过 true 并且不要调用 Class.forName ,你可以删除jar但是如果你没有跳过,即使你没有引用 classLoader classLoader = null ),在JVM退出之前无法删除jar。

In the below example, the jar contains a class called HelloWorld. What I do is try to load the class contained in the jar via a classLoader which adds the jar dynamically. If you set skip to true and do not call Class.forName, you can delete the jar but if you do not skip and even if you unreference the classLoader (classLoader = null), the jar cannot be deleted until the JVM exits.

为什么?

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();
    }

}


推荐答案

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

I have found an answer and a workaround.

基于此文章和这个惊人的相关文章,使用 Class.forName(className,true,classLoader)因为它使类无限期地缓存在内存中。

Based on this article and this amazing related article, it is a bad habit to use Class.forName(className, true, classLoader) because it keeps the class cached in the memory indefinitely.

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

The solution was to use classLoader.loadClass(clasName) instead, then once finished, unreference the classLoader and call the garbage collector using:

classLoader = null;
System.gc();

希望这有助于其他人! :)

Hope this helps others! :)

背景信息:

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

My project was a complexe one: we had a GWT server acting as a RMI client to another server. So to create instances, GWT needed to download the classes from the server and load them. Later, GWT would resend instance to the server to persist them in database using Hibernate. In order to support hot deployment, we opted for dynamically class loading where a user would upload a jar and notifies the server who would load the classes from it and present them as available to GWT server

这篇关于Java classLoader困境与锁定的罐子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆