-Xbootclasspath:bootclasspath
讓jvm從指定路徑(可以是分號分隔的目錄、jar、或者zip)中加載bootclass,用來替換jdk的rt.jar;若非必要,一般不會用到;
-Xbootclasspath/a:path
將指定路徑的所有文件追加到默認bootstrap路徑中;
-Xbootclasspath/p:path
讓jvm優先於bootstrap默認路徑加載指定路徑的所有文件;
一,ClassLoader的大體過程
如圖:
詳解:
虛擬機一啟動,會先做一些初始化的動作。一旦初始化動作完成之后,就會產生第一個類別加載器,即所謂的Bootstrap Loader,Bootstrap Loader 是由C++ 所撰寫而成,這個Bootstrap Loader所做的初始工作中,除了也做一些基本的初始化動作之外,最重要的就是加載定義在sun.misc 命名空間底下的Launcher.java 之中的ExtClassLoader( 因為是inner class ,所以編譯之后會變成Launcher$ExtClassLoader.class) ,並設定其Parent 為null,代表其父加載器為Bootstrap Loader 。然后Bootstrap Loader ,再要求加載定義於sun.misc 命名空間底下的Launcher.java 之中的AppClassLoader( 因為是inner class,所以編譯之后會變成Launcher$AppClassLoader.class) ,並設定其Parent 為之前產生的ExtClassLoader 實例。
由以上可以看出,classLoader是由下向上查找,上層的不能向下查找。
二,ClassLoader中類的關系
如圖:
詳解:
AppClassLoader 和ExtClassLoader 都是URLClassLoader 的子類別。由於它們都是URLClassLoader 的子類別,所以它們也應該有URL 作為搜尋類別檔的參考,由原始碼中我們可以得知,AppClassLoader 所參考的URL 是從系統參java.class.path 取出的字符串所決定,而java.class.path 則是由我們在執行java.exe 時,利用 –cp 或-classpath 或CLASSPATH 環境變量所決定。
ClassLoader的loadClass代碼:
- protected synchronized Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException
- {
- // First, check if the class has already been loaded
- //類是否被加載過
- Class c = findLoadedClass(name);
- if (c == null) {
- try {
- if (parent != null) {
- //到parentclassloader中去查找(像這個parent還有parent遞歸方式進行查找)
- c = parent.loadClass(name, false);
- } else {
- //啟動類加載器進行加載
- c = findBootstrapClass0(name);
- }
- } catch (ClassNotFoundException e) {
- // If still not found, then invoke findClass in order
- // to find the class.
- //當一直都沒有找到時,啟動當前類的findClass進行查找
- //這個通常也是我們擴展的地方
- c = findClass(name);
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded //類是否被加載過 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //到parentclassloader中去查找(像這個parent還有parent遞歸方式進行查找) c = parent.loadClass(name, false); } else { //啟動類加載器進行加載 c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // If still not found, then invoke findClass in order // to find the class. //當一直都沒有找到時,啟動當前類的findClass進行查找 //這個通常也是我們擴展的地方 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
三,分析及證明:
可以用最底層的ClassLoader得到某一個類(Test)時,Test.class.getClassLoader()就可知當前類在哪一個層次的ClassLoader下被加載
1,BootStrapClassLoader
- Class clazz=Class.forName("java.lang.Object");
- System.out.println(clazz.getClassLoader());
- //輸出為null,因為bootstrap在java中不是類,而是由c++編寫的
- URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
- for (int i = 0; i < urls.length; i++) {
- System.out.println(urls[i].getFile());
- }//用這個進行查找bootstrap所加載的是哪些jar包
Class clazz=Class.forName("java.lang.Object"); System.out.println(clazz.getClassLoader()); //輸出為null,因為bootstrap在java中不是類,而是由c++編寫的 URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs(); for (int i = 0; i < urls.length; i++) { System.out.println(urls[i].getFile()); }//用這個進行查找bootstrap所加載的是哪些jar包
2,ExtClassLoader
- clazz = Class.forName("sun.net.spi.nameservice.dns.DNSNameService");
- clazzLoader = clazz.getClassLoader();
- System.out.println(" sun.net.spi.nameservice.dns.DNSNameService's loader is "
- + clazzLoader);
- //在些可以說明ExtClassLoader所加載的類
clazz = Class.forName("sun.net.spi.nameservice.dns.DNSNameService"); clazzLoader = clazz.getClassLoader(); System.out.println(" sun.net.spi.nameservice.dns.DNSNameService's loader is " + clazzLoader); //在些可以說明ExtClassLoader所加載的類
3,AppClassLoader
當前工程中class與lib都是用此Loader加載
可以通過ClassLoader.getSystemClassLoader()可以獲取到AppClassLoader的
4,DefineClassLoader
可以繼承URLClassLoader或ClassLoader
當繼承ClassLoader重寫findClass()方法,parent會相應是AppClassLoader-->ExtClassLoader-->BootStrapClassLoader
URLClassLoader可以直接設置url即可
問題:
由於自己自定義了一個DefineClassLoader替代了加載ant的ClassLoader,另外添加自己jar包,
但是在執行ant編譯時,要執行tools.jar里的javac類,在執行javac這個類時,是處在AppClassLoader
下,找不到我添加的DefineClassLoader的jar包
------------------------------------------------
其他一些相應的操作(參考)
在預設情況下,AppClassLoader的搜尋路徑為”.”( 目前所在目錄),如果使用-classpath 選項(與-cp 等效),就可以改變AppClassLoader 的搜尋路徑,如果沒有指定-classpath 選項,就會搜尋環境變量CLASSPATH 。如果同時有CLASSPATH 的環境設定與-classpath 選項,則以-classpath 選項的內容為主,CLASSPATH 的環境設定與-classpath 選項兩者的內容不會有加成的效果。至於ExtClassLoader 也有相同的情形,不過其搜尋路徑是參考系統參數java.ext.dirsBootstrap Loader ,我們可以經由查詢由系統參數sun.boot.class.path 得知Bootstrap Loader 用來搜尋類別的路徑java -Dsun.boot.class.path=請回頭看到java.class.path 與sun.boot.class.path,也就是說,AppClassLoader 與Bootstrap Loader 會搜尋它們所指定的位置(或JAR 文件),如果找不到就找不到了,AppClassLoader 與Bootstrap Loader 不會遞歸式地搜尋這些位置下的其他路徑或其他沒有被指定的JAR 文件。反觀ExtClassLoader,所參考的系統參數是java.ext.dirs,意思是說,他會搜尋底下的所有JAR 文件以及classes 目錄,作為其搜尋路徑(所以您會發現上面我們在測試的時候,如果加入-Dsun.boot.class.path=c:windows 選項時,程序的起始速度會慢了些,這是因為c:windows 目錄下的文件很多,必須花額外的時間來列舉JAR 文件)。
在命令行下參數時,使用–classpath / -cp / 環境變量CLASSPATH 來更改AppClassLoader 的搜尋路徑,或者用 –Djava.ext.dirs 來改變ExtClassLoader的搜尋目錄,兩者都是有意義的。可是用–Dsun.boot.class.path 來改變Bootstrap Loader 的搜尋路徑是無效。這是因為AppClassLoader與ExtClassLoader 都是各自參考這兩個系統參數的內容而建立,當您在命令行下變更這兩個系統參數之后,AppClassLoader 與ExtClassLoader在建立實例的時候會參考這兩個系統參數,因而改變了它們搜尋類別文件的路徑; 而系統參數sun.boot.class.path 則是默認與Bootstrap Loader 的搜尋路徑相同,就算您更改該系統參與,與BootstrapLoader 完全無關,AppClassLoader 與ExtClassLoader 在整個虛擬機之中只會存有一份,一旦建立了,其內部所參考的搜尋路徑將不再改變,也就是說,即使我們在程序里利用System.setProperty() 來改變系統參數的內容,仍然無法更動AppClassLoader 與ExtClassLoader 的搜尋路徑。因此,執行時期動態更改搜尋路徑的設定是不可能的事情。