深刻探究jvm之類裝載器

1、class裝載驗證流程java

一、加載bootstrap

  1)、取得類的二進制流。設計模式

  2)、轉爲方法區數據結構。安全

  3)、在Java堆中生成對應的java.lang.Class對象。數據結構

二、連接--驗證(目的:保證Class流的格式是正確的)app

  1)、文件格式的驗證:是不是0xCAFEBASE開頭、版本號是否正確等。框架

  2)、元數據驗證:是否有父類、是否繼承了final類、非抽象類是否實現了全部的抽象方法等。jvm

  3)、字節碼驗證(最複雜):運行檢查、棧數據類型和操做碼數據參數是否吻合、跳轉指令是否指定到合理的位置。ide

  4)、符號引用驗證:常量池中描述類是否存在、訪問的字段和方法是否存在且有足夠的權限。模塊化

三、連接--準備

  分配內存,併爲類設置初始值(在方法區中),舉個栗子:

  ①public static int a = 100;在連接--準備階段中,a會被設置爲0(int 類型的默認值),在初始化的<clinit>中才會被設置爲1。

  ②public static final int a = 100;對於static final 類型,在連接--準備階段就會被賦上正確的值,a被設置爲100。

四、連接--解析

  符號引用(字符串引用對象不必定被加載)替換爲直接引用(指針或者偏移量,引用對象必定存在於內存中)。

五、初始化

  1)、執行類構造器<clinit>,包括static變量賦值語句和static塊。、

  2)、子類的<clinit>調用前保證父類的<clinit>被調用。

  3)、<clinit>方法是線程安全的,同步執行。

2、什麼是ClassLoader?

  ClassLoader是一個抽象類,它的實例將讀入的Java字節碼裝載到jvm中,ClassLoader能夠實現定製,以知足不一樣的字節碼流的獲取方式,主要負責類裝載過程當中的加載。

   1)ClassLoader中重要的方法:

public Class<?> loadClass(String name) throws ClassNotFoundException
載入並返回一個Class

protected final Class<?> defineClass(byte[] b, int off, int len)
定義一個類,不公開調用

protected Class<?> findClass(String name) throws ClassNotFoundException
loadClass回調該方法,自定義ClassLoader的推薦作法

protected final Class<?> findLoadedClass(String name) 
尋找已經加載的類

   2)JDK中ClassLoader默認的設計模式分類:

   ①BootStrap ClassLoader(啓動ClassLoader)

   ②Extension ClassLoader (擴展ClassLoader)

   ③App ClassLoader(應用ClassLoader/系統ClassLoader)

   ④自定義ClassLoader

   上圖展示了類查找和加載的次序,這樣咱們很容易就會想到存在一個問題:頂層的ClassLoader是沒法加載底層ClassLoader的類,只就是「雙親問題」。那麼Java框架(也就是rt.jar)如何加載Classpath下應用的類呢?

  舉個栗子進一步說明問題:javax.xml.parsers包中定義了xml解析的類接口,這些類接口(Service Provider Interface)都位於rt.jar,即接口的定義(以及類的工廠方法)都在Bootstrap ClassLoader中,其實這個SPI的實現(非抽象類)都在AppLoader之中,JDK要求Bootstrap ClassLoader可以加載Classpath下的類,這顯然是不知足上圖的要求的。

  JDK爲了解決這個問題在Thread類中定義了一個靜態方法,Thread.setContextClassLoader()。這是一個上下文加載器,是一個「角色」,並非一個真正的ClassLoader,它只是承擔了特殊的任務,用以解決頂層ClassLoader沒法訪問底層ClassLoader的類的問題。基本思想是在頂層ClassLoader中傳入一個底層的ClassLoader實例。下面的代碼來源於rt.jar中javax.xml.parsers.FactoryFinder展現如何在啓動類加載器中加載AppLoader的類突破「雙親模式」問題。

static private Class getProviderClass(String className, ClassLoader cl,
        boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{
    try {
        if (cl == null) {
            if (useBSClsLoader) {
                return Class.forName(className, true, FactoryFinder.class.getClassLoader());
            } else {
                cl = ss.getContextClassLoader();
                if (cl == null) {
                    throw new ClassNotFoundException();
                }
                else {
                    return cl.loadClass(className); //使用上下文ClassLoader
                }
            }
        }
        else {
            return cl.loadClass(className);
        }
    }
    catch (ClassNotFoundException e1) {
        if (doFallback) {
            // Use current class loader - should always be bootstrap CL
            return Class.forName(className, true, FactoryFinder.class.getClassLoader());
        }
…..

  雙親模式是默認的模式,但不是必需要這麼作,好比Tomcat的WebappClassLoader就會先加載本身的class,找不到再委託parent,再如,OSGi(模塊化,熱加載)的ClassLoader造成網狀結構,根據須要自由加載Class。

  破壞雙親模式的例子,先從底層的ClassLoader加載。OrderClassLoader的部分實現:

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    // First, check if the class has already been loaded
    Class re=findClass(name);
    if(re==null){
        System.out.println(「沒法載入類:」+name+「 須要請求父加載器");
        return super.loadClass(name,resolve);
    }
    return re;
}

  findClass(String name )實現以下:

protected Class<?> findClass(String className) throws ClassNotFoundException {
Class clazz = this.findLoadedClass(className);//每一個類只加載一次,會查找、定義、加載
if (null == clazz) {
    try {
        String classFile = getClassFile(className);
        FileInputStream fis = new FileInputStream(classFile);
        FileChannel fileC = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel outC = Channels.newChannel(baos);
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
         //-----------------省略部分代碼-------------------//
        fis.close();
        byte[] bytes = baos.toByteArray();

        clazz = defineClass(className, bytes, 0, bytes.length);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
return clazz;
}
相關文章
相關標籤/搜索