Java類載入機制的深入研究,java機制深入研究
類載入機制
類載入是Java程式啟動並執行首要步驟,研究類的載入有助於瞭解JVM執行過程,並指導開發人員採取更有效措施配合程式執行,同時讓程式能動態控制類載入,比如熱部署等,提高程式的靈活性和適應性。
類載入過程
Java程式啟動並執行場所是記憶體。
當在命令列執行java HelloWorld的時候,JVM會將HelloWorld.class載入到記憶體中,形成一個class對象:HelloWorld.class,具體過程如下:
1.找到jre目錄,找到jvm.dll檔案,並且初始化JVM
2.產生一個Bootstrap Loader(引導程式,輕量級的應用程式,它定義了應用程式之間想要共用的類)
3.Bootstrap Loader自動載入Extended Loader(標準擴充類載入器),並將其父Loader設為Bootstrap Loader
4.Bootstrap Loader自動載入AppClass Loader(系統類別載入器),並將其父Loader設為Extended Loader。
5.最後由AppClass Loader載入HelloWorld類
Bootstrap Loader——>Extended Loader——>AppClass Loader
以上就是類載入的最一般的過程。
類載入器各自搜尋的目錄
1、Bootstrap Loader(啟動類載入器):載入System.getProperty(“sun.boot.class.path”)所指定的路徑或jar。
2、Extended Loader(標準擴充類載入器ExtClassLoader):載入System.getProperty(“java.ext.dirs”)所指定的路徑或jar。在使用Java運行程式時,也可以指定其搜尋路徑,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld
3、AppClass Loader(系統類別載入器AppClassLoader):載入System.getProperty(“java.class.path”)所指定的路徑或jar。在使用Java運行程式時,也可以加上-cp來覆蓋原有的Classpath設定,例如: java -cp ./lavasoft/classes HelloWorld
ExtClassLoader和AppClassLoader在JVM啟動後,會在JVM中儲存一份,並且在程式運行中無法改變其搜尋路徑。如果想在運行時從其他搜尋路徑載入類,就要產生新的類載入器。
類載入器的特點
1、運行一個程式時,總是由AppClass Loader(系統類別載入器)開始載入指定的類。
2、在載入類時,每個類載入器會將載入任務上交給其父,如果其父找不到,再由自己去載入。
3、Bootstrap Loader(啟動類載入器)是最頂級的類載入器了,其父載入器為null.
類載入器的擷取
看下面例子
public class HelloWorldClass { public static void main(String[] args) { HelloWorldClass hello = new HelloWorldClass(); Class c= hello.getClass(); ClassLoader loader = c.getClassLoader(); System.out.println("loader:"+loader); System.out.println("loader.getParent():"+loader.getParent()); System.out.println("loader.getParent().getParent():"+loader.getParent().getParent()); }}
運行結果:
loader:sun.misc.Launcher$AppClassLoader@47415dbfloader.getParent():sun.misc.Launcher$ExtClassLoader@1471cb25loader.getParent().getParent():null
從上面的結果可以看出,並沒有擷取到ExtClassLoader的父Loader,原因是Bootstrap Loader(啟動類載入器)是用C語言實現的,找不到一個確定的返回父Loader的方式,於是就返回null。
類的載入
類載入有三種方式:
1、命令列啟動應用時候由JVM初始化載入
2、通過Class.forName()方法動態載入
3、通過ClassLoader.loadClass()方法動態載入
三種方式區別比較大,看個例子就明白了:
public class HelloWorld { public static void main(String[] args) throws ClassNotFoundException { ClassLoader loader = HelloWorld.class.getClassLoader(); System.out.println(loader); //使用ClassLoader.loadClass()來載入類,不會執行初始化塊 loader.loadClass("Test2"); //使用Class.forName()來載入類,預設會執行初始化塊 // Class.forName("Test2"); //使用Class.forName()來載入類,並指定ClassLoader,初始化時不執行靜態塊 // Class.forName("Test2", false, loader); } }public class Test2 { static { System.out.println("靜態初始化塊執行了!"); } }
分別切換載入方式,會有不同的輸出結果。
自訂ClassLoader
為了說明問題,先看例子:
package test; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; /** * 自訂ClassLoader * * */ public class MyClassLoader { public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException { URL url = new URL( "file:/E:\\projects\\testScanner\\out\\production\\testScanner"); ClassLoader myloader = new URLClassLoader(new URL[] { url }); Class c = myloader.loadClass("test.Test3"); System.out.println("----------"); Test3 t3 = (Test3) c.newInstance(); }}public class Test3 { static { System.out.println("Test3的靜態初始化塊執行了!"); }}
運行後:
Test3的靜態初始化塊執行了!
Process finished with exit code 0
可以看出自訂了ClassLoader myloader = new URLClassLoader(new URL[]{url});已經成功將類Test3載入到記憶體了,並通過預設構造方法構造了對象Test3 t3 = (Test3) c.newInstance();
有關ClassLoader還有很重要一點:
同一個ClassLoader載入的類檔案,只有一個Class執行個體。但是,如果同一個類檔案被不同的ClassLoader載入,則會有兩份不同的ClassLoader執行個體(前提是著兩個類載入器不能用相同的父類載入器)。
引用:
1.love_Javc_you的專欄:深入研究Java類載入機制
2.sysu_2010的專欄:BootStrap Loader
著作權聲明:本文為CSDN博主Lunatictwo原創文章,未經博主允許不得轉載。