java類載入器——ClassLoader

來源:互聯網
上載者:User

標籤:java類載入器   classloader   雙親委派模型   全盤負責機制   classnotfoundexcepti   

Java的設計初衷是主要面向嵌入式領域,對於自訂的一些類,考慮使用依需求載入原則,即在程式使用到時才載入類,節省記憶體消耗,這時即可通過類載入器來動態載入。

如果你平時只是做web開發,那應該很少會跟類載入器打交道,但如果你想深入學習tomcat伺服器的架構,它是必不可少的。所謂類載入器,就是用於載入Java類到Java虛擬機器中,它負責讀取Java位元組碼,並轉換成java.lang.Class類的一個執行個體,使位元組代碼.class檔案得以運行。一般類載入器負責根據一個指定的類找到對應的位元組代碼,然後根據這些代碼定義成一個Java類,另外還負責載入資源,包括影像檔和設定檔。

類載入器在實際使用中給我們帶來的好處是,它可以使Java類動態地載入到JVM並運行,即可在程式運行時再載入類,提供了很靈活的動態載入方式。例如我們熟悉的Applet,從遠程伺服器下載位元組碼到用戶端動態載入到JVM便可以運行。

在Java的龐大體系中,可以將系統分為三種類載入器,分別是:

①   啟動類載入器(Bootstrap ClassLoader):載入對象是Java核心庫,把一些關鍵的Java類載入進JVM,這個載入器使用原生代碼(C/C++)實現的,並不是繼承java.lang.ClassLoader,它是所有其他類載入器的最終父載入器,負責載入<JAVA_HOME>/jre/lib目錄下且被JVM指定的類庫,其實它屬於JVM整體的一部分,JVM一啟動就將這些指定的類載入到記憶體中,避免以後過多的I/O操作,提高系統的運行效率。啟動類載入器無法被Java程式直接使用。

②   擴充類載入器(Extension ClassLoader):載入的對象為Java的擴充庫,即載入<JAVA_HOME>/jre/lib/ext目錄裡面的類。這個類由上面的Bootstrap ClassLoader載入,但由於Bootstrap ClassLoader並非用Java實現,已經脫離了Java體系,所以如果嘗試調用擴充類載入器的getParent()方法擷取父載入器會得到null,但它的父類載入器是Bootstrap ClassLoader。Java中可以直接使用擴充類載入器。

③   應用程式類載入器(Application  ClassLoader):亦叫系統類別載入器(System ClassLoader),它負責載入使用者類路徑(CLASSPATH)指定的類庫,如果程式沒有自己定義類載入器,就預設使用應用程式類載入器。它也由Bootstrap ClassLoader載入,但它的父載入類被設定成了Extension ClassLoader。如果要使用這個載入器,可通過ClassLoader.getSystemClassLoader()擷取。

假如有一天你心血來潮也想自己寫一個類載入器,那麼你只需要繼承java.lang.ClassLoader類即可。於是可以用下面的圖2-4-1來清晰表示出各種類載入器的關係,Bootstrap ClassLoader是最根本的類載入器,其不存在父類載入器,Extension ClassLoader由Bootstrap ClassLoader載入,所以它的父類載入器是Bootstrap ClassLoader,Application ClassLoader也是由Bootstrap ClassLoader載入,但它的父載入器被指向了Extension ClassLoader,而其他所有使用者自訂的類載入器都由Application ClassLoader載入。

由此可以看出越重要的類載入器就越早被JVM載入,這是考慮到安全性,因為先載入的類載入器會充當下一個類載入器的父載入器,在雙親委派模型機制下,就能確保安全性。

那麼什麼是雙親委派模型?比如爺爺、爸爸、你三代單傳,你們家族都遺傳懶惰基因,每當遇到事情都互相推脫,現在需要一個人去買一包鹽,不然今晚的菜就有色無味了,而你第一個被委託去超市買,但由於你的懶惰,你向你爸爸撒了一頓嬌,你爸爸受不了你只能答應幫你去,在懶惰的驅使下,你爸爸最後找了一個借口說要趕工作,讓你爺爺出去走動走動順便買一包鹽,就這樣你爺爺只能乖乖去超市買鹽。雙親委派模型就類似這樣的機制,類載入器載入類時首先委託給父類載入器載入,除非父類載入器不能載入才自己載入。

這種模型要求除了頂層的啟動類載入器外,其他的類載入器都要有自己的父類載入器。假如有一個類要載入進來,一個類載入器並不會馬上嘗試自己將其載入,而是委派到父類載入器,父類載入器收到後又嘗試委派到其父類載入器,以此類推,直到委派到啟動類載入器,這樣一層一層往上委派。只有當父類載入器反饋自己沒法完成這個載入時,子載入器才會嘗試自己載入。通過這個機制,保證了Java應用所使用的都是同一個版本的Java核心庫的類。同時這個機制也保證了安全性,設想如果Application ClassLoader想要載入一個有破壞性的java.lang.System類,雙親委派模型會一層層向上委派,最終委派給Bootstrap ClassLoader,而Bootstrap ClassLoader檢查到緩衝中已經有了這個類,並不會再載入這個有破壞性的System類。

另外,類載入器還擁有全盤負責機制,即當一個ClassLoader載入一個類時,這個類所依賴的、引用的其他所有類都由這個ClassLoader載入,除非在程式中顯性地指定另外一個ClassLoader載入。

圖2-4-1 類載入器關係

 

在Java中,我們用完全符合類名來標識一個類,即用包名和類名。而在JVM中,一個類由完全符合類名和一個類載入器的執行個體ID作為唯一標識。即是說同一個虛擬機器可以有兩個包名、類名都相同的類,只要他們由兩個不同類載入器載入。於是當我們在Java中經常說兩個類相不相等,必須是針對同一個類載入器載入的前提下才有意義,否則就算是同樣的位元組代碼,由不同類載入器載入,這兩個類也不是相等的。這種特徵為我們提供了隔離機制,在tomcat伺服器中是十分有用的。

瞭解了JVM的類載入器的各種機制後,看看一個類是怎樣被類載入器載入進來的。一個類準備載入,類載入器先判斷此類是否已經被載入過(載入過的類會被緩衝在記憶體中),如果緩衝中存在此類則直接返回這個類。否則擷取父類載入器,如果父類載入器為null,則由Bootstrap ClassLoader載入並返回Class。如果父類載入器不為null,則由父類載入器載入,載入成功就返回Class,載入失敗則根據類路徑尋找class檔案,找到就載入此class並返回Class,找不到就拋出ClassNotFoundException異常。

圖2-4-2 類載入過程

類載入器屬於JVM層級的設計,我們很多時候基本不會跟他打交道,但是假如你想瞭解整個JAVA體系是如何工作的,假如你要設計開發自己的架構或中介軟體或一個較龐大的java軟體,那麼你必須熟悉類載入器的相關機制,在現實的設計中,根據實際情況利用類載入器可以提供類庫的隔離及共用,保證你的軟體不同層級的邏輯分割程式不會互相影響,提供更好的安全性。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.