1. 程式人生 > >Java內存管理-掌握虛擬機類加載器(五)

Java內存管理-掌握虛擬機類加載器(五)

線程安全 ssi 推薦 為我 認識 ade 作用 直接 ssp

勿在流沙築高臺,出來混遲早要還的。

做一個積極的人

編碼、改bug、提升自己

我有一個樂園,面向編程,春暖花開!

上一篇介紹虛擬機類加載機制,講解了類加載機制中的三個階段,分別是:加載、連接(驗證、準備、解析)、初始化 ,知道了類加載的機制。下面我們就要知道類到底是通過什麽方式加載到內存中的,也就是本文要介紹的類加載器,類加載器就是加載類的信息到內存中

本文地圖 :

技術分享圖片

一、什麽是類加載器(ClassLoader)

虛擬機設計團隊把類加載階段中的”通過一個類的全限定名來獲取描述此類的二進制字節流“這個動作是放到Java虛擬機外部去實現的,以便讓應用程序自己決定如何去獲取所需的類,實現這個動作的代碼模塊稱為”類加載器“。

說簡單一點:類加載器可以把類加載到Java虛擬機中,我們可以使用Java虛擬機自帶的類加載器,也可以自定義實現自己的類加載器(自定義類加載器會在後面文章進行講解)。

類加載器雖然只用於實現類的加載動作,但它在Java程序中起到的作用遠不限於類加載階段。對於任意的一個類,都需要由加載它的類加載器和這個類本身一同確立起在Java虛擬機中的唯一性每個類加載器都擁有一個獨立的類命名空間(命名空間下面單獨會介紹)。

說簡單一點:比較兩個類是否”相等“,只有在這兩個類是由同一個類加載器加載的前提在才有意義,否則,即使這兩個類源於同一個Class文件,被同一個虛擬機加載,只要加載它們的類加載器不同,那這兩個類必定不相等。這裏所指的“相等”包括代表類的Class對象的equal方法、isAssignableFrom()、isInstance()方法及instance關鍵字返回的結果。

技術分享圖片

如上圖,此時雖然是同一個Person.class ,但是被不同的類加載器加載到Java虛擬機,那麽加載後的這兩個 Person類是不”相等“,因為它們分別在兩個不同的命名空間中。

註:如果上面兩個類的是否”相等“比較你沒看懂的話,後面自定義類加載器的時候我們會演示一下這個例子,進行簡單說明。

二、類加載器分類

一張圖看懂類加載器分類:

技術分享圖片

上圖的類加載器主要分為四類:

  • Bootstrap ClassLoader : 啟動類加載器
  • Extension ClassLoader : 擴展類加載器
  • Application ClassLoader :應用程序類加載器
  • User ClassLoader :自定義類加載器

從虛擬機角度分析,類加載器分為兩類: 一種是啟動類加載器(Bootstrap ClassLoader),這個類加載器使用C++語言實現,是虛擬機自身的一部分;另一種是其他類加載器,這些類加載器是Java語言實現的,獨立於虛擬機之外,都繼承自抽象類java.lang.ClassLoader。

public class ClassLoaderDemo {

    public static void main(String[] args) {

        // ClassLoaderDemo 的類加載器
        System.out.println(ClassLoaderDemo.class.getClassLoader());

        // 打印每個 ClassLoader的父加載器
        ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
        while (classLoader != null){
            System.out.println(classLoader);
            classLoader = classLoader.getParent();
        }
        System.out.println(classLoader);
    }
}
--- 打印結果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d
null

啟動類加載器(根加載器)

主要負責加載存放在JAVA_HOME/jre/lib/rt.jar裏面所有的class文件,或者被-Xbootclasspath參數所指定路徑中以rt.jar命名的文件。啟動類加載器無法被Java程序直接引用,如果在編寫自定義類加載時,需要把加載的請求委派給啟動類加載器,那麽直接使用null代替即可。

例如:

MyClassLoader myClassLoader= new MyClassLoader(null); //父加載器使用啟動類加載器
public MyClassLoader(ClassLoader parent){
    super(parent);//指定該類加載器的父類加載器
}

擴展類加載器

這個加載器由sun.misc.Launcher$ExtClassLoader實現,它負責加載AVA_HOME/lib/ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫。

應用程序類加載器

這個加載器由sun.misc.Launcher$AppClassLoader實現,它負責加載classpath對應的jar及目錄。如果應用程序沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。

自定義類加載器

用戶定制自己的類加載器,繼承ClassLoader,根據自己的需要進行設計和實現,比如需要從指定的路徑下讀取class文件進行加載等。

知道上面這些內容,你就應該聯想到為什麽在學習Java的時候首先要配置Java環境變量了。這就是知其所以然的過程。

思考:這麽多類加載器,那麽如何保證一個類不被重復加載,只被加載一次呢?

從JDK1.2版本開始,類加載過程中采用了雙親委派模型(父親委派機制),這種機制能更好的保證Java平臺的安全,在此委托機制中,除了Java虛擬機自帶的根類加載(根加載器最頂級,無父加載器)以外,其余的類加載器都有且只有一個父加載器。雙親委派模型的工作過程是:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到頂層的啟動類加載器中,只有父類加載器反饋自己無法完成這個加載請求(它搜索的範圍中沒有找打所需的類)時,子加載器才會嘗試自己去加載。

如下示意圖:

技術分享圖片

在說明一下: 加載一個類的時候。首先自定義類加載類器先會委派給應用程序類加載器(Application ClassLoader),應用程序類加載器會委派給擴展類加載器(Extension ClassLoader),擴展類加載器會委派給啟動類加載器(Bootstrap ClassLoader)去加載,這時候如果啟動類加載器加載成功,則加載結束。如果加載失敗,則交給擴展類加載器去加載,如果擴展類加載器加載成功,則加載結束。如果加載失敗,則交給應用程序類加載器,如果應用程序類加載器加載成功,則加載結束。如果加載失敗,則交給自定義類加載器。如果自定義類加載器加載成功,則加載結束。否則加載失敗,會報ClassNotFoundExecption異常,結束。

每個類加載器都有自己的管轄範圍(命名空間),並在自己的管轄範圍做好自己的事情

雙親委派模型還有一個優點是能提供軟件系統的安全性,在這個機制下,用戶自定義的類加載器不可能加載應該由父類加載器加載的可靠類,從而防止不可靠甚至是惡意的代碼代替父類加載器加載可靠代碼。如 java.land.Object 類總是由啟動類加載器進行加載,其他任何用戶自定義的類加載器都不可能加載含有惡意代碼的java.land.Object 類。(每一個看似簡單的設計背後,都是大師們智慧的結晶


tips: 類加載器命名空間

每個類加載器都有自己的命名空間,命名空間由該加載器及所有父類加載器所加載的類組成。在同一個命名空間中,不會出現兩個全類名(包名+類名)完全一樣的類;在不同的命名空間中,有可能出現全類名相同的兩個類。

技術分享圖片

一定要註意是:有可能出現全類名一樣的,就如 Loader1 中自定義類加載器可以加載如 com.aflyun.HelloJVM.java , Loader2 中自定義類加載器也可以加載如 com.aflyun.HelloJVM.java 的類,兩個互不影響,並且比較兩個類的話,是不”相等“的。並且不同命名空間中類加載器加載的類,不能訪問其他類加載器包中的包可見(即默認訪問級別)成員。

三、總結

本文主要介紹了什麽是類加載器以及類加載器的分類 ,讓大家對類加載器相關的知識有一個整體的認識,這樣我們也知道了哪些類加載器加載什麽地方的數據。為我們後面對類加載器源碼和分析以及實現自定義類加載器做好鋪墊。預告:下一篇文章講一下類加載器的源碼分析和設計模式,以及實現一個自定義類加載器!

四、參考資料

《深入理解Java虛擬機》

推薦閱讀

Java的線程安全、單例模式、JVM內存結構等知識梳理
Java內存管理-程序運行過程(一)
Java內存管理-初始JVM和JVM啟動流程(二)
Java內存管理-JVM內存模型以及JDK7和JDK內存模型對比總結(三)


謝謝你的閱讀,如果您覺得這篇博文對你有幫助,請點贊或者喜歡,讓更多的人看到!祝你每天開心愉快!


不管做什麽,只要堅持下去就會看到不一樣!在路上,不卑不亢!

博客首頁 : http://blog.csdn.net/u010648555

願你我在人生的路上能都變成最好的自己,能夠成為一個獨擋一面的人技術分享圖片

© 每天都在變得更好的阿飛雲

Java內存管理-掌握虛擬機類加載器(五)