簡體   English   中英

Java classLoader困境與鎖定的罐子

[英]Java classLoader dilemma with locked jars

我正在玩Java中的classLoaders並注意到一件奇怪的事情。 如果classLoader從jar加載一個類,即使你沒有引用你的classLoader,這個jar也會無限期地被鎖定。

在下面的示例中,jar包含一個名為HelloWorld的類。 我所做的是嘗試通過classLoader加載jar中包含的類,該類動態地添加jar。 如果將skip設置為true並且不調用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)是一個壞習慣Class.forName(className, true, classLoader)因為它使類無限期地緩存在內存中。

解決方案是使用classLoader.loadClass(clasName) ,然后完成后, classLoader.loadClass(clasName)引用classLoader並使用以下命令調用垃圾收集器:

classLoader = null;
System.gc();

希望這有助於他人! :)

背景資料:

我的項目很復雜:我們有一台GWT服務器充當另一台服務器的RMI客戶端。 因此,要創建實例,GWT需要從服務器下載類並加載它們。 稍后,GWT會將實例重新發送到服務器,以使用Hibernate將它們保存在數據庫中。 為了支持熱部署,我們選擇動態類加載,用戶上傳jar並通知服務器誰將從中加載類並將它們呈現給GWT服務器

在Java 7中, URLClassLoader有一個#close()方法來修復它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM