Java類加載機制(二)

jopen 8年前發布 | 7K 次閱讀 Java開發

類加載器原理

將class文件字節碼內容加載到內存中,并將這些靜態數據轉換為方法區的運行時數據結構,在堆中生成一個代表這個類的java.lang.Class對象,作為方法區類數據訪問的入口

類緩存 標準的JavaSE類記載器可以按照要求查找類,但一旦某個類被加載到類加載器中, 它將維持加載(緩存)一段時間。不過,JVM垃圾回收器可以回收這些Claas對象。

類加載器樹狀結構、雙親委托機制

類加載器樹狀結構

  • 引導類加載器    用來加載Java的核心庫(JAVA_HOME/jre/lib/rt/jar,或sun.boot.class.path路徑    下的內容),是用原生的代碼(c++)實現的,并不繼承java.lang.ClassLoader。    加載擴展類加載器和應用程序類加載器。并指定它們的父類加載器。

  • 擴展類記載器     用來加載Java的擴展庫(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路徑下的內容)。 Java虛擬機的實現會提供一個擴展庫目錄。該類加載器在此目錄里面查找并加載Java類。

  • 應用程序類加載器     它根據Java應用的類路徑(classpath,java.class.path路類)    一般來說,Java應用的類都是由它來完成加載的。由sun.misc.Launcher$AppClassLoader實現。

  • 自定義類加載器    開發人員可以通過繼承java.lang.ClassLoader類的方式實現自己的類加載器,以滿足一些特殊的需要。

類加載器圖示

public class Demo {
    public static void main(String[] args) {
        //獲取應用程序類加載器
        System.out.println(ClassLoader.getSystemClassLoader());
        //獲取擴展類加載器
        System.out.println(ClassLoader.getSystemClassLoader().getParent());
        //獲取引導類加載器
        System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
        //獲取classpath
        System.out.println(System.getProperty("java.class.path"));
    }
}

雙親委托機制

  • 代理模式:交給其他類加載器加載指定的類

  • 雙親委托機制

    (1)就是當某個特定的類加載器接到加載類的請求的時候,首先委托給其父類(父類如果有父類一直向上追溯),直到父類加載器無法加載時,該加載器進行加載。

    (2)雙親委托機制是為了保證Java核心庫的類型安全。

                這種機制保證不會加載到用戶自定義的java.lang.Class類的情況

    (3)類加載器除了用于加載類,也是安全最基本的屏障。

雙親委托機制是代理模式的一種,但是并不是所有的類加載都是雙親委托機制,比如tomcat類加載器首先嘗試特定的 類加載器,加載不到類時在嘗試器父類加載器。

自定義類加載器

如何實現自定義類加載器:
(1)繼承java.lang.ClassLoader
(2)檢查所請求的類型是否已經被這個類加載器加載到命名空間,如果已經被加載直接返回。
(3)委派給父類加載(也可以不委派,這個程序控制)。
(4)調用自定義加載器findClass()方法獲取字節碼,然后調用defineClass()導入類型到方法區。
public class FileSystemClassLoader extends ClassLoader {
    String rootDir;

    public FileSystemClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class c = findLoadedClass(name);
        if (c != null) {
            return c;
        } else {
            ClassLoader parent = this.getParent();
            try {
                c = parent.loadClass(name);
            }catch (Exception e){
                e.printStackTrace();
            }
            if (c != null) {
                return c;
            } else {
                byte[] classData = getClassData(name);
                if (classData == null) {
                    throw new ClassNotFoundException("自定義類加載器沒有加載到");
                } else {
                    c = defineClass(name, classData, 0, classData.length);
                }
            }
        }
        return c;
    }

    private byte[] getClassData(String className) {

        String path = rootDir + "/" + className.replace(".", "/") + ".class";
        InputStream is = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            is = new FileInputStream(path);

            byte[] buffer = new byte[1024];
            int temp = 0;
            while ((temp = is.read(buffer)) != -1) {
                baos.write(buffer,0,temp);
            }

            return baos.toByteArray();
        } catch (Exception e) {
            return null;
        } finally {
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(baos != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

public class TestMyClassLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        FileSystemClassLoader loader = new FileSystemClassLoader("/Users/wjk/Desktop");
        FileSystemClassLoader loader1 = new FileSystemClassLoader("/Users/wjk/Desktop");
        Class c = loader.loadClass("com.Hello");
        Class c1 = loader1.loadClass("com.Hello");
        Class c2 = loader.loadClass("com.Hello");
        Class c3 = loader.loadClass("java.lang.String");


        System.out.println(c.hashCode());//被兩個類加載器加載的同一個類,JVM認為是不同的(c和c1的hashCode值不一樣)
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());


        System.out.println(c.getClassLoader());//使用的是自定義的類加載器
        System.out.println(c3.getClassLoader());//使用的是引導類加載器
    }
}
//結果
1725154839
1670675563
1725154839
classLoaderTest.FileSystemClassLoader@5e2de80c
null

線程上下文類加載器

服務器類加載器原理和OSGI介紹

來自: http://my.oschina.net/u/2361475/blog/603798

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!