深入研究Java类加载机制
生活随笔
收集整理的這篇文章主要介紹了
深入研究Java类加载机制
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
深入研究Java類(lèi)加載機(jī)制
?
類(lèi)加載是Java程序運(yùn)行的第一步,研究類(lèi)的加載有助于了解JVM執(zhí)行過(guò)程,并指導(dǎo)開(kāi)發(fā)者采取更有效的措施配合程序執(zhí)行。
研究類(lèi)加載機(jī)制的第二個(gè)目的是讓程序能動(dòng)態(tài)的控制類(lèi)加載,比如熱部署等,提高程序的靈活性和適應(yīng)性。
?
一、簡(jiǎn)單過(guò)程
?
Java程序運(yùn)行的場(chǎng)所是內(nèi)存,當(dāng)在命令行下執(zhí)行:
java HelloWorld
命令的時(shí)候,JVM會(huì)將HelloWorld.class加載到內(nèi)存中,并形成一個(gè)Class的對(duì)象HelloWorld.class。
其中的過(guò)程就是類(lèi)加載過(guò)程:
1、尋找jre目錄,尋找jvm.dll,并初始化JVM;
2、產(chǎn)生一個(gè)Bootstrap Loader(啟動(dòng)類(lèi)加載器);
3、Bootstrap Loader自動(dòng)加載Extended Loader(標(biāo)準(zhǔn)擴(kuò)展類(lèi)加載器),并將其父Loader設(shè)為Bootstrap Loader。
4、Bootstrap Loader自動(dòng)加載AppClass Loader(系統(tǒng)類(lèi)加載器),并將其父Loader設(shè)為Extended Loader。
5、最后由AppClass Loader加載HelloWorld類(lèi)。
?
以上就是類(lèi)加載的最一般的過(guò)程。
?
二、類(lèi)加載器各自搜索的目錄
?
為了弄清楚這個(gè)問(wèn)題,首先還要看看System類(lèi)的API doc文檔。
?
?
?
可惜這個(gè)幫助文檔并不全,直接用程序打印出來(lái)如下:
????????????????for?(Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {?
????????????????????????System.out.println(entry.getKey()+"\t"+entry.getValue());?
????????????????} ?
?
1、Bootstrap Loader(啟動(dòng)類(lèi)加載器):加載System.getProperty("sun.boot.class.path")所指定的路徑或jar。
2、Extended Loader(標(biāo)準(zhǔn)擴(kuò)展類(lèi)加載器ExtClassLoader):加載System.getProperty("java.ext.dirs")所指定的路徑或jar。在使用Java運(yùn)行程序時(shí),也可以指定其搜索路徑,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld ? 3、AppClass Loader(系統(tǒng)類(lèi)加載器AppClassLoader):加載System.getProperty("java.class.path")所指定的路徑或jar。在使用Java運(yùn)行程序時(shí),也可以加上-cp來(lái)覆蓋原有的Classpath設(shè)置,例如: java -cp ./lavasoft/classes HelloWorld ? ExtClassLoader和AppClassLoader在JVM啟動(dòng)后,會(huì)在JVM中保存一份,并且在程序運(yùn)行中無(wú)法改變其搜索路徑。如果想在運(yùn)行時(shí)從其他搜索路徑加載類(lèi),就要產(chǎn)生新的類(lèi)加載器。 ? 三、類(lèi)加載器的特點(diǎn) ? 1、運(yùn)行一個(gè)程序時(shí),總是由AppClass Loader(系統(tǒng)類(lèi)加載器)開(kāi)始加載指定的類(lèi)。 2、在加載類(lèi)時(shí),每個(gè)類(lèi)加載器會(huì)將加載任務(wù)上交給其父,如果其父找不到,再由自己去加載。
3、Bootstrap Loader(啟動(dòng)類(lèi)加載器)是最頂級(jí)的類(lèi)加載器了,其父加載器為null. ? 四、類(lèi)加載器的獲取 ? 很容易,看下面例子 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());?
????????}?
} ? 打印結(jié)果: sun.misc.Launcher$AppClassLoader@19821f?
sun.misc.Launcher$ExtClassLoader@addbf1?
null?
Process finished with exit code 0 ? 從上面的結(jié)果可以看出,并沒(méi)有獲取到ExtClassLoader的父Loader,原因是Bootstrap Loader(啟動(dòng)類(lèi)加載器)是用C語(yǔ)言實(shí)現(xiàn)的,找不到一個(gè)確定的返回父Loader的方式,于是就返回null。 ? 五、類(lèi)的加載 ? 類(lèi)加載有三種方式: 1、命令行啟動(dòng)應(yīng)用時(shí)候由JVM初始化加載 2、通過(guò)Class.forName()方法動(dòng)態(tài)加載 3、通過(guò)ClassLoader.loadClass()方法動(dòng)態(tài)加載 ? 三種方式區(qū)別比較大,看個(gè)例子就明白了: public?class?HelloWorld {?
????????public?static?void?main(String[] args)?throws?ClassNotFoundException {?
????????????????ClassLoader loader = HelloWorld.class.getClassLoader();?
????????????????System.out.println(loader);?
????????????????//使用ClassLoader.loadClass()來(lái)加載類(lèi),不會(huì)執(zhí)行初始化塊?
????????????????loader.loadClass("Test2");?
????????????????//使用Class.forName()來(lái)加載類(lèi),默認(rèn)會(huì)執(zhí)行初始化塊?
//????????????????Class.forName("Test2");?
????????????????//使用Class.forName()來(lái)加載類(lèi),并指定ClassLoader,初始化時(shí)不執(zhí)行靜態(tài)塊?
//????????????????Class.forName("Test2", false, loader);?
????????}?
} ? public?class?Test2 {?
????????static?{?
????????????????System.out.println("靜態(tài)初始化塊執(zhí)行了!");?
????????}?
} ? 分別切換加載方式,會(huì)有不同的輸出結(jié)果。 ? 六、自定義ClassLoader ? 為了說(shuō)明問(wèn)題,先看例子: 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的靜態(tài)初始化塊執(zhí)行了!");?
????????}?
} ? 運(yùn)行后: ----------?
Test3的靜態(tài)初始化塊執(zhí)行了!?
Process finished with exit code 0 ? 可以看出自定義了ClassLoader myloader = new URLClassLoader(new URL[]{url});已經(jīng)成功將類(lèi)Test3加載到內(nèi)存了,并通過(guò)默認(rèn)構(gòu)造方法構(gòu)造了對(duì)象Test3 t3 = (Test3) c.newInstance(); ? 有關(guān)ClassLoader還有很重要一點(diǎn): 同一個(gè)ClassLoader加載的類(lèi)文件,只有一個(gè)Class實(shí)例。但是,如果同一個(gè)類(lèi)文件被不同的ClassLoader載入,則會(huì)有兩份不同的ClassLoader實(shí)例(前提是著兩個(gè)類(lèi)加載器不能用相同的父類(lèi)加載器)。
| 鍵 | 相關(guān)值的描述 |
| java.version | Java 運(yùn)行時(shí)環(huán)境版本 |
| java.vendor | Java 運(yùn)行時(shí)環(huán)境供應(yīng)商 |
| java.vendor.url | Java 供應(yīng)商的 URL |
| java.home | Java 安裝目錄 |
| java.vm.specification.version | Java 虛擬機(jī)規(guī)范版本 |
| java.vm.specification.vendor | Java 虛擬機(jī)規(guī)范供應(yīng)商 |
| java.vm.specification.name | Java 虛擬機(jī)規(guī)范名稱(chēng) |
| java.vm.version | Java 虛擬機(jī)實(shí)現(xiàn)版本 |
| java.vm.vendor | Java 虛擬機(jī)實(shí)現(xiàn)供應(yīng)商 |
| java.vm.name | Java 虛擬機(jī)實(shí)現(xiàn)名稱(chēng) |
| java.specification.version | Java 運(yùn)行時(shí)環(huán)境規(guī)范版本 |
| java.specification.vendor | Java 運(yùn)行時(shí)環(huán)境規(guī)范供應(yīng)商 |
| java.specification.name | Java 運(yùn)行時(shí)環(huán)境規(guī)范名稱(chēng) |
| java.class.version | Java 類(lèi)格式版本號(hào) |
| java.class.path | Java 類(lèi)路徑 |
| java.library.path | 加載庫(kù)時(shí)搜索的路徑列表 |
| java.io.tmpdir | 默認(rèn)的臨時(shí)文件路徑 |
| java.compiler | 要使用的 JIT 編譯器的名稱(chēng) |
| java.ext.dirs | 一個(gè)或多個(gè)擴(kuò)展目錄的路徑 |
| os.name | 操作系統(tǒng)的名稱(chēng) |
| os.arch | 操作系統(tǒng)的架構(gòu) |
| os.version | 操作系統(tǒng)的版本 |
| file.separator | 文件分隔符(在 UNIX 系統(tǒng)中是“/”) |
| path.separator | 路徑分隔符(在 UNIX 系統(tǒng)中是“:”) |
| line.separator | 行分隔符(在 UNIX 系統(tǒng)中是“/n”) |
| user.name | 用戶的賬戶名稱(chēng) |
| user.home | 用戶的主目錄 |
| user.dir | 用戶的當(dāng)前工作目錄 |
????????????????????????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(標(biāo)準(zhǔn)擴(kuò)展類(lèi)加載器ExtClassLoader):加載System.getProperty("java.ext.dirs")所指定的路徑或jar。在使用Java運(yùn)行程序時(shí),也可以指定其搜索路徑,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld ? 3、AppClass Loader(系統(tǒng)類(lèi)加載器AppClassLoader):加載System.getProperty("java.class.path")所指定的路徑或jar。在使用Java運(yùn)行程序時(shí),也可以加上-cp來(lái)覆蓋原有的Classpath設(shè)置,例如: java -cp ./lavasoft/classes HelloWorld ? ExtClassLoader和AppClassLoader在JVM啟動(dòng)后,會(huì)在JVM中保存一份,并且在程序運(yùn)行中無(wú)法改變其搜索路徑。如果想在運(yùn)行時(shí)從其他搜索路徑加載類(lèi),就要產(chǎn)生新的類(lèi)加載器。 ? 三、類(lèi)加載器的特點(diǎn) ? 1、運(yùn)行一個(gè)程序時(shí),總是由AppClass Loader(系統(tǒng)類(lèi)加載器)開(kāi)始加載指定的類(lèi)。 2、在加載類(lèi)時(shí),每個(gè)類(lèi)加載器會(huì)將加載任務(wù)上交給其父,如果其父找不到,再由自己去加載。
3、Bootstrap Loader(啟動(dòng)類(lèi)加載器)是最頂級(jí)的類(lèi)加載器了,其父加載器為null. ? 四、類(lèi)加載器的獲取 ? 很容易,看下面例子 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());?
????????}?
} ? 打印結(jié)果: sun.misc.Launcher$AppClassLoader@19821f?
sun.misc.Launcher$ExtClassLoader@addbf1?
null?
Process finished with exit code 0 ? 從上面的結(jié)果可以看出,并沒(méi)有獲取到ExtClassLoader的父Loader,原因是Bootstrap Loader(啟動(dòng)類(lèi)加載器)是用C語(yǔ)言實(shí)現(xiàn)的,找不到一個(gè)確定的返回父Loader的方式,于是就返回null。 ? 五、類(lèi)的加載 ? 類(lèi)加載有三種方式: 1、命令行啟動(dòng)應(yīng)用時(shí)候由JVM初始化加載 2、通過(guò)Class.forName()方法動(dòng)態(tài)加載 3、通過(guò)ClassLoader.loadClass()方法動(dòng)態(tài)加載 ? 三種方式區(qū)別比較大,看個(gè)例子就明白了: public?class?HelloWorld {?
????????public?static?void?main(String[] args)?throws?ClassNotFoundException {?
????????????????ClassLoader loader = HelloWorld.class.getClassLoader();?
????????????????System.out.println(loader);?
????????????????//使用ClassLoader.loadClass()來(lái)加載類(lèi),不會(huì)執(zhí)行初始化塊?
????????????????loader.loadClass("Test2");?
????????????????//使用Class.forName()來(lái)加載類(lèi),默認(rèn)會(huì)執(zhí)行初始化塊?
//????????????????Class.forName("Test2");?
????????????????//使用Class.forName()來(lái)加載類(lèi),并指定ClassLoader,初始化時(shí)不執(zhí)行靜態(tài)塊?
//????????????????Class.forName("Test2", false, loader);?
????????}?
} ? public?class?Test2 {?
????????static?{?
????????????????System.out.println("靜態(tài)初始化塊執(zhí)行了!");?
????????}?
} ? 分別切換加載方式,會(huì)有不同的輸出結(jié)果。 ? 六、自定義ClassLoader ? 為了說(shuō)明問(wèn)題,先看例子: 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的靜態(tài)初始化塊執(zhí)行了!");?
????????}?
} ? 運(yùn)行后: ----------?
Test3的靜態(tài)初始化塊執(zhí)行了!?
Process finished with exit code 0 ? 可以看出自定義了ClassLoader myloader = new URLClassLoader(new URL[]{url});已經(jīng)成功將類(lèi)Test3加載到內(nèi)存了,并通過(guò)默認(rèn)構(gòu)造方法構(gòu)造了對(duì)象Test3 t3 = (Test3) c.newInstance(); ? 有關(guān)ClassLoader還有很重要一點(diǎn): 同一個(gè)ClassLoader加載的類(lèi)文件,只有一個(gè)Class實(shí)例。但是,如果同一個(gè)類(lèi)文件被不同的ClassLoader載入,則會(huì)有兩份不同的ClassLoader實(shí)例(前提是著兩個(gè)類(lèi)加載器不能用相同的父類(lèi)加載器)。
總結(jié)
以上是生活随笔為你收集整理的深入研究Java类加载机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 解析 Java 类和对象的初始化过程 由
- 下一篇: 深入理解Java:类加载机制及反射