深入研究Java类加载机制
生活随笔
收集整理的這篇文章主要介紹了
深入研究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類。
?
以上就是類加載的最一般的過程。
?
二、類加載器各自搜索的目錄
?
為了弄清楚這個問題,首先還要看看System類的API doc文檔。
?
?
?
可惜這個幫助文檔并不全,直接用程序打印出來如下:
????????????????for?(Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {?
????????????????????????System.out.println(entry.getKey()+"\t"+entry.getValue());?
????????????????} ?
?
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?HelloWorld {?
????????public?static?void?main(String[] args) {?
????????????????HelloWorld hello =?new?HelloWorld();?
????????????????Class c = hello.getClass();?
????????????????ClassLoader loader = c.getClassLoader();?
????????????????System.out.println(loader);?
????????????????System.out.println(loader.getParent());?
????????????????System.out.println(loader.getParent().getParent());?
????????}?
} ? 打印結果: sun.misc.Launcher$AppClassLoader@19821f?
sun.misc.Launcher$ExtClassLoader@addbf1?
null?
Process finished with exit code 0 ? 從上面的結果可以看出,并沒有獲取到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?
*?
* @author leizhimin 2009-7-29 22:05:48?
*/?
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實例(前提是著兩個類加載器不能用相同的父類加載器)。
| 鍵 | 相關值的描述 |
| java.version | Java 運行時環境版本 |
| java.vendor | Java 運行時環境供應商 |
| java.vendor.url | Java 供應商的 URL |
| java.home | Java 安裝目錄 |
| java.vm.specification.version | Java 虛擬機規范版本 |
| java.vm.specification.vendor | Java 虛擬機規范供應商 |
| java.vm.specification.name | Java 虛擬機規范名稱 |
| java.vm.version | Java 虛擬機實現版本 |
| java.vm.vendor | Java 虛擬機實現供應商 |
| java.vm.name | Java 虛擬機實現名稱 |
| java.specification.version | Java 運行時環境規范版本 |
| java.specification.vendor | Java 運行時環境規范供應商 |
| java.specification.name | Java 運行時環境規范名稱 |
| java.class.version | Java 類格式版本號 |
| java.class.path | Java 類路徑 |
| java.library.path | 加載庫時搜索的路徑列表 |
| java.io.tmpdir | 默認的臨時文件路徑 |
| java.compiler | 要使用的 JIT 編譯器的名稱 |
| java.ext.dirs | 一個或多個擴展目錄的路徑 |
| os.name | 操作系統的名稱 |
| os.arch | 操作系統的架構 |
| os.version | 操作系統的版本 |
| file.separator | 文件分隔符(在 UNIX 系統中是“/”) |
| path.separator | 路徑分隔符(在 UNIX 系統中是“:”) |
| line.separator | 行分隔符(在 UNIX 系統中是“/n”) |
| user.name | 用戶的賬戶名稱 |
| user.home | 用戶的主目錄 |
| user.dir | 用戶的當前工作目錄 |
????????????????????????System.out.println(entry.getKey()+"\t"+entry.getValue());?
????????????????} ?
| java.runtime.name | Java(TM) SE Runtime Environment |
| sun.boot.library.path | Q:\jdk6\jre\bin |
| java.vm.version | 14.0-b16 |
| java.vm.vendor | Sun Microsystems Inc. |
| java.vendor.url | http://java.sun.com/ |
| path.separator | ; |
| idea.launcher.port | 7532 |
| java.vm.name | Java HotSpot(TM) Client VM |
| file.encoding.pkg | sun.io |
| sun.java.launcher | SUN_STANDARD |
| user.country | CN |
| sun.os.patch.level | Service Pack 3 |
| java.vm.specification.name | Java Virtual Machine Specification |
| user.dir | E:\projects\testScanner |
| java.runtime.version | 1.6.0_14-b08 |
| java.awt.graphicsenv | sun.awt.Win32GraphicsEnvironment |
| java.endorsed.dirs | Q:\jdk6\jre\lib\endorsed |
| os.arch | x86 |
| java.io.tmpdir | C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ |
| line.separator | |
| java.vm.specification.vendor | Sun Microsystems Inc. |
| user.variant | |
| os.name | Windows XP |
| sun.jnu.encoding | GBK |
| java.library.path | Q:\jdk6\bin;.;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;Q:\jdk6\bin;Q:\JavaFX\javafx-sdk1.2\bin;Q:\JavaFX\javafx-sdk1.2\emulator\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\MySQL Server 5.1\bin;C:\Program Files\StormII\Codec;C:\Program Files\StormII |
| java.specification.name | Java Platform API Specification |
| java.class.version | 50 |
| sun.management.compiler | HotSpot Client Compiler |
| os.version | 5.1 |
| user.home | d:\我的文檔 |
| user.timezone | |
| java.awt.printerjob | sun.awt.windows.WPrinterJob |
| idea.launcher.bin.path | C:\IDEA8\bin |
| file.encoding | UTF-8 |
| java.specification.version | 1.6 |
| java.class.path | Q:\jdk6\jre\lib\alt-rt.jar;Q:\jdk6\jre\lib\charsets.jar;Q:\jdk6\jre\lib\deploy.jar;Q:\jdk6\jre\lib\javaws.jar;Q:\jdk6\jre\lib\jce.jar;Q:\jdk6\jre\lib\jsse.jar;Q:\jdk6\jre\lib\management-agent.jar;Q:\jdk6\jre\lib\plugin.jar;Q:\jdk6\jre\lib\resources.jar;Q:\jdk6\jre\lib\rt.jar;Q:\jdk6\jre\lib\ext\dnsns.jar;Q:\jdk6\jre\lib\ext\localedata.jar;Q:\jdk6\jre\lib\ext\sunjce_provider.jar;Q:\jdk6\jre\lib\ext\sunmscapi.jar;Q:\jdk6\jre\lib\ext\sunpkcs11.jar;E:\projects\testScanner\out\production\testScanner;C:\IDEA8\lib\idea_rt.jar |
| user.name | Administrator |
| java.vm.specification.version | 1 |
| java.home | Q:\jdk6\jre |
| sun.arch.data.model | 32 |
| user.language | zh |
| java.specification.vendor | Sun Microsystems Inc. |
| awt.toolkit | sun.awt.windows.WToolkit |
| java.vm.info | mixed mode, sharing |
| java.version | 1.6.0_14 |
| java.ext.dirs | Q:\jdk6\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext |
| sun.boot.class.path | Q:\jdk6\jre\lib\resources.jar;Q:\jdk6\jre\lib\rt.jar;Q:\jdk6\jre\lib\sunrsasign.jar;Q:\jdk6\jre\lib\jsse.jar;Q:\jdk6\jre\lib\jce.jar;Q:\jdk6\jre\lib\charsets.jar;Q:\jdk6\jre\classes |
| java.vendor | Sun Microsystems Inc. |
| file.separator | \ |
| java.vendor.url.bug | http://java.sun.com/cgi-bin/bugreport.cgi |
| sun.io.unicode.encoding | UnicodeLittle |
| sun.cpu.endian | little |
| sun.desktop | windows |
| sun.cpu.isalist |
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?HelloWorld {?
????????public?static?void?main(String[] args) {?
????????????????HelloWorld hello =?new?HelloWorld();?
????????????????Class c = hello.getClass();?
????????????????ClassLoader loader = c.getClassLoader();?
????????????????System.out.println(loader);?
????????????????System.out.println(loader.getParent());?
????????????????System.out.println(loader.getParent().getParent());?
????????}?
} ? 打印結果: sun.misc.Launcher$AppClassLoader@19821f?
sun.misc.Launcher$ExtClassLoader@addbf1?
null?
Process finished with exit code 0 ? 從上面的結果可以看出,并沒有獲取到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?
*?
* @author leizhimin 2009-7-29 22:05:48?
*/?
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實例(前提是著兩個類加載器不能用相同的父類加載器)。
總結
以上是生活随笔為你收集整理的深入研究Java类加载机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解析 Java 类和对象的初始化过程 由
- 下一篇: 细谈 对象的初始化过程------内存中