JVM 類加載器介紹及其父親委托機制 Parent Delegation
原文出處: arthinking
1、類加載器:
類加載器用來把類加載到Java虛擬機中。從JDK1.2版本開始,類的加載過程采用父親委托機制,這種機制能更好的保證Java平臺的安全。在此委托機制中,除了Java虛擬機自帶的根類加載器之外,其余的類加載器都有且只有一個父加載器。當Java程序請求加載器loader1加載Sample類是,loader1類首先委托自己的父加載器去加載Sample類,若父加載器能加載,則由父加載器完成加載任務,否則才由加載器loader1本身加載Sample類。
2、類加載器的父親委托機制(Parent Delegation):
2.1、Java虛擬機自帶了以下幾種加載器:
根(Bootstrap)類加載器:該加載器沒有父加載器。它負責加載虛擬機的核心類庫,如java.lang.*等。例如java.lang.Object就是由根類加載器加載的。根類加載器從系統屬性sun.boot.class.path所指定的目錄中加載類庫。根類加載器的實現依賴于底層操作系統,屬于虛擬機的實現的一部分,它并沒有繼承java.lang.ClassLoader類。
擴展(Extension)類加載器:它的父加載器為根類加載器。它從java.ext.dirs系統屬性所指定的目錄中加載類庫,或者從JDK的安裝目錄的jre/lib/ext子目錄(擴展目錄)下加載類庫,如果把用戶創建的JAR文件放在這個目錄下,也會自動由擴展類加載器加載。擴展類加載器是純Java類,是java.lang.ClassLoader類的子類。
系統(System)類加載器:也稱為應用類加載器,它的父加載器為擴展類加載器。它從環境變量classpath或者系統屬性java.class.path所指定的目錄中加載類,它是用戶自定義的類加載器的默認父加載器。系統類加載器是純Java類,是java.lang.ClassLoader類的子類。
父子加載器并非繼承關系,也就是說子加載器不一定是繼承了父加載器。
除了以上虛擬機自帶的加載器以外,用戶還可以定制自己的類加載器(User-defined Class Loader)。Java提供了抽象類java.lang.ClassLoader,所有用戶自定義的類加載器應該繼承ClassLoader類。
根類加載器 <– 擴展類加載器 <– 系統類加載器 <– 用戶自定義加載器
在父親委托機制中,各個加載器按照父子關系形成了樹形結構,除了根類加載器以外,其余的類加載器都有且只有一個父加載器。
Class sampleClass = loader2.loadClass("Sample");
loader2首先從自己的命名空間中查找Sample類是否已經被加載,如果已經加載,就直接返回代表Sample類的Class對象的引用。
如果Sample類還沒有被加載,loader2首先請求loader1代為加載,loader1再請求系統類加載器代為加載,系統類加載器再請求擴展類加載器代為架子啊,擴展類加載器在請求根類加載器代為加載。若根加載器和擴展加載器都不能加載,則系統類加載器嘗試加載,若能加載成功,則將Sample類所對應的Class對象的引用返回給loader1,loader1再將引用返回給loader2,從而成功將Sample類加載進虛擬機。若系統類加載器不能加載Sample類,則loader1嘗試加載Sample類,若laoder1也不能成功加載,則loader2嘗試加載。若所有的父加載器及laoder2本身都不能加載,則拋出ClassNotFoundException異常。
加載器之間的父子關系實際上指的是加載器對象之間的包裝關系,而不是類之間的繼承關系。一對父子加載器可能是同一個加載器類的兩個實例,也可能不是。在子加載器對象中包裝了一個父加載器對象
ClassLoader loader1 = new MyClassLoader(); //參數loader1將作為loader2的父加載器 ClassLoader loader2 = new MyClassLoader(loader1);
如果某個類加載器能夠加載一個類,那么該類加載器就稱作定義類加載器;定義類加載器及其所有子類加載器都稱作初始類加載器。
假設loader1實際加載了Sample類,則loader1為Sample類的定義類加載器,laoder2和loader1為Sample類的初始類加載器。
ClassLoader protected ClassLoader(ClassLoader parent)
使用指定的、用于委托操作的父類加載器創建新的類加載器。
如果存在安全管理器,則調用其 checkCreateClassLoader 方法。這可能導致安全性異常。
參數: parent – 父類加載器
拋出: SecurityException – 如果存在安全管理器并且其 checkCreateClassLoader 方法不允許創建新的類加載器。
當生成一個自定義的類加載器實例時,如果沒有指定它的父加載器,那么系統類加載器就將成為該類加載器的父加載器。
父委托機制的優點是能夠提高軟件系統的安全性。因為在此機制下,用戶自定義的類加載器不可能加載應該由父加載器加載的可靠類,從而防止不可靠甚至惡意的代碼代替由父加載器加載的可靠代碼。例如,java.lang.Object類總是由根類加載器加載,其他任何用戶自定義的類加載器都不可能加載含有惡意代碼的java.lang.Object類。
每個類加載器都有自己的命名空間,命名空間由該加載器及所有父加載器所加載的類組成。在同一個命名空間中,不會出現類的完整名字(包括類的包名)相同的兩個類;在不同的命名空間中,有可能會出現類的完整名字(包括類的包名)相同的兩個類。
由同一類加載器加載的屬于相同包的類組成了運行時包。決定兩個類是不是屬于同一個運行時包,不僅要看它們的包名是否相同,還要看定義類加載器是否相同。只有屬于同一運行時報的類才能相互訪問包可見(即默認訪問級別)的類和成員。這樣的限制能避免用戶自定義的類冒充核心類庫的類,去訪問核心類庫的包可見成員。假設用戶自己定義了一個java.lang.Spy,并用用戶自定義的類加載器加載,由于java.lang.Spy和核心類庫java.lang.*由不同的加載器加載,它們屬于不同的運行時包,所以java.lang.Spy不能訪問核心類庫java.lang包中的包可見成員。