JVM 類加載器介紹及其父親委托機制 Parent Delegation

jopen 9年前發布 | 8K 次閱讀 JVM Java開發

原文出處: 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類的子類。

父子加載器并非繼承關系,也就是說子加載器不一定是繼承了父加載器。

2.2、自定義類加載器:

除了以上虛擬機自帶的加載器以外,用戶還可以定制自己的類加載器(User-defined Class Loader)。Java提供了抽象類java.lang.ClassLoader,所有用戶自定義的類加載器應該繼承ClassLoader類。

2.3、類加載器的關系:

根類加載器 <– 擴展類加載器 <– 系統類加載器 <– 用戶自定義加載器

在父親委托機制中,各個加載器按照父子關系形成了樹形結構,除了根類加載器以外,其余的類加載器都有且只有一個父加載器。

2.4、父加載的父親委托機制:

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);
2.5、定義類加載器:

如果某個類加載器能夠加載一個類,那么該類加載器就稱作定義類加載器;定義類加載器及其所有子類加載器都稱作初始類加載器

假設loader1實際加載了Sample類,則loader1為Sample類的定義類加載器,laoder2和loader1為Sample類的初始類加載器。

ClassLoader protected ClassLoader(ClassLoader parent)

使用指定的、用于委托操作的父類加載器創建新的類加載器。

如果存在安全管理器,則調用其 checkCreateClassLoader 方法。這可能導致安全性異常。

參數: parent – 父類加載器

拋出: SecurityException – 如果存在安全管理器并且其 checkCreateClassLoader 方法不允許創建新的類加載器。

當生成一個自定義的類加載器實例時,如果沒有指定它的父加載器,那么系統類加載器就將成為該類加載器的父加載器。

2.6、類加載器的父委托機制的安全性:

父委托機制的優點是能夠提高軟件系統的安全性。因為在此機制下,用戶自定義的類加載器不可能加載應該由父加載器加載的可靠類,從而防止不可靠甚至惡意的代碼代替由父加載器加載的可靠代碼。例如,java.lang.Object類總是由根類加載器加載,其他任何用戶自定義的類加載器都不可能加載含有惡意代碼的java.lang.Object類。

2.7、命名空間:

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

2.8、運行時包:

由同一類加載器加載的屬于相同包的類組成了運行時包。決定兩個類是不是屬于同一個運行時包,不僅要看它們的包名是否相同,還要看定義類加載器是否相同。只有屬于同一運行時報的類才能相互訪問包可見(即默認訪問級別)的類和成員。這樣的限制能避免用戶自定義的類冒充核心類庫的類,去訪問核心類庫的包可見成員。假設用戶自己定義了一個java.lang.Spy,并用用戶自定義的類加載器加載,由于java.lang.Spy和核心類庫java.lang.*由不同的加載器加載,它們屬于不同的運行時包,所以java.lang.Spy不能訪問核心類庫java.lang包中的包可見成員。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!