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