ClassLoad的加載過程及分析


-Xbootclasspath:bootclasspath 
讓jvm從指定路徑(可以是分號分隔的目錄、jar、或者zip)中加載bootclass,用來替換jdk的rt.jar;若非必要,一般不會用到; 
-Xbootclasspath/a:path 
將指定路徑的所有文件追加到默認bootstrap路徑中; 
-Xbootclasspath/p:path 
讓jvm優先於bootstrap默認路徑加載指定路徑的所有文件; 

 

一,ClassLoader的大體過程

 

如圖:

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代碼:

 

 

Java代碼 復制代碼  收藏代碼
  1. protected synchronized Class<?> loadClass(String name, boolean resolve)  
  2.     throws ClassNotFoundException  
  3.     {  
  4.     // First, check if the class has already been loaded  
  5.        //類是否被加載過  
  6.     Class c = findLoadedClass(name);  
  7.     if (c == null) {  
  8.         try {  
  9.         if (parent != null) {  
  10.                   //到parentclassloader中去查找(像這個parent還有parent遞歸方式進行查找)  
  11.             c = parent.loadClass(name, false);  
  12.         } else {  
  13.                   //啟動類加載器進行加載  
  14.             c = findBootstrapClass0(name);  
  15.         }  
  16.         } catch (ClassNotFoundException e) {  
  17.             // If still not found, then invoke findClass in order  
  18.             // to find the class.  
  19.               //當一直都沒有找到時,啟動當前類的findClass進行查找  
  20.              //這個通常也是我們擴展的地方  
  21.             c = findClass(name);  
  22.         }  
  23.     }  
  24.     if (resolve) {  
  25.         resolveClass(c);  
  26.     }  
  27.     return c;  
  28.     }  
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;
    }

 

 
詳解:
java文件的編譯和Class的載入執行,都是使用Launcher初始化的appclassloader作為類載入器的,我們無法動態的改變App classloader,更無法讓JVM使用我們自己的classloader來替換system classloader,根據全盤負責原則,就限制了編譯和運行時,我們無法直接顯式的使用一個system classloader尋找不到的Class,即我們只能使用Java核心類庫,擴展類庫和CLASSPATH中的類庫中的Class。
而且我們也無法載入以java.lang....開頭的包,進行了限制

 

 

三,分析及證明:

可以用最底層的ClassLoader得到某一個類(Test)時,Test.class.getClassLoader()就可知當前類在哪一個層次的ClassLoader下被加載

 

1,BootStrapClassLoader

 

Java代碼 復制代碼  收藏代碼
  1. Class clazz=Class.forName("java.lang.Object");  
  2. System.out.println(clazz.getClassLoader());  
  3.   
  4.   //輸出為null,因為bootstrap在java中不是類,而是由c++編寫的  
  5.   
  6.   
  7.     URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();  
  8.          for (int i = 0; i < urls.length; i++) {  
  9.            System.out.println(urls[i].getFile());  
  10.          }//用這個進行查找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

 

Java代碼 復制代碼  收藏代碼
  1. clazz = Class.forName("sun.net.spi.nameservice.dns.DNSNameService");    
  2.             clazzLoader = clazz.getClassLoader();    
  3.             System.out.println(" sun.net.spi.nameservice.dns.DNSNameService's loader is  "    
  4.                             + clazzLoader);    
  5. //在些可以說明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 的搜尋路徑。因此,執行時期動態更改搜尋路徑的設定是不可能的事情。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM