關於ClassLoader的知識點記錄

JVM 啓動的時候會產生3個ClassLoader,他們分別負責加載不同目錄下的class。其中Bootstrap ClassLoader 是 C++ 實現,如果 parent ClassLoader是它的話,獲取的時候是會返回null 的。其中 System ClassLoader 也就是我們所說的 AppClassLoader。

  1. Bootstrap ClassLoader/啓動類加載器
    主要負責jdk_home/lib目錄下的核心 api 或 -Xbootclasspath 選項指定的jar包裝入工作。
  2. Extension ClassLoader/擴展類加載器
    主要負責jdk_home/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包裝入工作。
  3. System ClassLoader/系統類加載器
    主要負責java -classpath/-Djava.class.path所指的目錄下的類與jar包裝入工作。
  4. User Custom ClassLoader/用戶自定義類加載器(java.lang.ClassLoader的子類)
    在程序運行期間, 通過java.lang.ClassLoader的子類動態加載class文件, 體現java動態實時類裝入特性。

關於類加載順序是 採用“雙親委派”加載鏈模式的。用戶自定義的 ClassLoader 是通過繼承 抽象ClassLoader 來實現。

這種加載機制可以避免重複加載並保障java核心類的加載安全性。

 

而線程上下文加載器 則可以打破這種 “雙親委派”加載鏈模式。

 

每個線程都有一個關聯的上下文Classloader。如果使用new Thread()方式生成新的線程,新線程將繼承其父線程的上下文Classloader。如果程序對線程上下文Classloader沒有任何改動的話,程序中所有的線程將都使用System Classloader作爲上下文Classloader。

 

當使用Thread.currentThread().setContextClassLoader(classloader)時,線程上下文 Classloader就變成了指定的Classloader了。此時,在本線程的任意一處地方,調用 Thread.currentThread().getContextClassLoader(),都可以得到前面設置的Classloader。

 

回到JAXP的例子,假設xercesImpl.jar只有AClassLoader能裝載,現在A.class內部要使用JAXP,但是A.class卻不是由AClassLoader或者它的子Classloader裝載的,那麼在A.class中,應該這樣寫才能正確得到xercesImpl的實現:

AClassLoader aClassLoader = new AClassLoader(parent);
Thread.currentThread().setContextClassLoader(aClassLoader);
SAXParserFactory factory = SAXParserFactory.getInstance();
...

 

JAXP這時就可以通過線程上下文Classloader裝載xercesImpl的實現類了,當然,還有一個前提是在配製文件或啓動參數中指定了使用xerces作爲JAXP的實現。下面是JAXP中的代碼片斷:

ClassLoader cl = Thread.currentThread().getContextClassLoader();
…
Class providerClass = cl.loadClass(className);

 

 

Java默認的線程上下文類加載器是 系統類加載器(AppClassLoader)。

使用線程上下文類加載器, 可以在執行線程中, 拋棄雙親委派加載鏈模式, 使用線程上下文裏的類加載器加載類.
典型的例子有, 通過線程上下文來加載第三方庫jndi實現, 而不依賴於雙親委派.
大部分java app服務器(jboss, tomcat..)也是採用contextClassLoader來處理web服務。
還有一些採用 hotswap 特性的框架, 也使用了線程上下文類加載器, 比如 seasar (full stack framework in japenese).

http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html  這裏是關於tomcat 容器的類加策略。

http://www.javaworld.com/javaqa/2003-06/01-qa-0606-load.html  這裏是一片關於 java Context Loader的應用說明。

 

static塊在什麼時候執行?

  • 當調用forName(String)載入class時執行,如果調用ClassLoader.loadClass並不會執行.forName(String,false,ClassLoader)時也不會執行.
  • 如果載入Class時沒有執行static塊則在第一次實例化時執行.比如new ,Class.newInstance()操作
  • static塊僅執行一次

各個java類由哪些classLoader加載?

  • java類可以通過實例.getClass.getClassLoader()得知
  • 接口由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()獲得實例)載入
  • ClassLoader類由bootstrap loader載入

類裝載的方式

類裝載的方式主要有兩種:顯式的和隱式的。

  • 顯式類裝載

    發生在使用以下方法調用進行裝載類的時候:

    • ClassLoader.loadClass()(使用指定的Classloader進行裝載)

    • Class.forName()(使用當前類的Caller Classloader進行裝載)

      當調用上述方法的時候,指定的Class(以類名爲參數)由Classloader裝入。這兩個方法的行爲有輕微的區別,Class.forName()在類裝載完成後,會對類進行初始化,而ClassLoader.loadClass()只負責裝載類。

  • 隱式類裝載

    發生在由於引用、實例化或繼承導致需要裝載類的時候。隱式類裝載是在幕後啓動的,JVM會解析必要的引用並裝載類。

    類的裝載通常組合了顯式和隱式兩種方式。例如,Classloader可能先顯式地裝載一個類,然後再隱式地裝載它引用的其它類。

  • 類裝載發生的時間

    從類裝載方式的描述中我們可以看到,只有在顯式的調用方法或者實例化、引用、繼承一個類時,類才真正被裝載。由此,我們可以知道,import並不會導致類裝載,以及,在一個類實例化之前,調用它的靜態方法,會導致這個類和它的父類、實現的接口和相關的靜態成員的類會被裝載,而它的成員變量的類卻不會被裝載

父裝載器不能夠引用子裝載器Class,

 

JVM中類的唯一性

JVM 爲每一個Classloader維護一個唯一標識。在一個JVM裏(對應一個Java進程),可以由不同的Classloader裝載多個同名的類(指包名和類名都完全相同,下同),爲了唯一地標識被不同Classloader裝載的類,JVM會在被裝載的類名前加上裝載該類的Classloader的標識。

 

 

摘抄 :

http://blog.chenlb.com/2009/06/java-classloader-architecture.html

http://infocenter.apusic.com/help/index.jsp?topic=/aas/v6/ch27s01.html

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章