類加載機制與雙親委派

虛擬機將描述類的數據從字節碼文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終造成能夠被虛擬機直接使用的 Java 類型,這就是虛擬機的類加載機制。類加載器負責將字節碼文件中的二進制數據加載到內存中,在運行時數據區的方法區內建立 Class 對象,這也是類加載過程的最終產物。類型的加載、連接、初始化的過程都是在程序運行時完成的。java

什麼是類加載器?

虛擬機設計團隊把類加載階段中的 經過一個類的全限定名來獲取描述此類的二進制字節流 這個動做放到 Java 虛擬機外部去實現,以便讓應用程序本身決定如何獲取所須要的類,實現這個動做的代碼模塊稱爲 類加載器。類加載器雖然只用於實現類的加載動做,但它在 Java 程序中起到的做用卻遠遠不限於類加載階段。對於任意一個類,都須要由加載它的類加載器和這個類自己一同確立在 Java 虛擬機中的惟一性。安全

類的生命週期

類從被加載到虛擬機內存中開始,到卸出內存爲止,它的整個生命週期包括:加載、驗證、準備、解析、初始化、使用、卸載 7 個階段。其中 驗證、準備、解析 3 個部分統稱爲連接,這七個階段的發生順序如圖所示: 數據結構

類的聲明週期

加載階段

加載 是類加載過程的一個階段。在加載階段,虛擬機須要完成如下三件事情:編輯器

  1. 經過一個類的徹底限定名來獲取定義此類的二進制字節流
  2. 將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構
  3. 在內存中生成一個表明這個類的 java.lang.Class 對象,做爲方法區這個類的各類數據的訪問入口

驗證階段

驗證 是鏈接階段的第一步,這一階段的目的是爲了確保字節碼文件的字節流中包含的信息符合當前虛擬機的要求,而且不會危害虛擬機自身的安全。爲何會有這個階段?由於字節碼文件並不必定要求用 Java 源碼編譯而來,可使用任何途徑產生,甚至包括用十六進制編輯器直接編寫來產生字節碼文件。驗證階段大體會完成下面 4 個階段的檢驗動做:設計

  1. 文件格式驗證。這一階段要驗證字節流是否符合字節碼文件格式的規範,而且能被當前版本的虛擬機處理。
  2. 元數據驗證。這一階段是對字節碼描述的信息進行語義分析,以保證其描述的信息符合 Java 語言規範的要求。
  3. 字節碼驗證。這一階段主要目的是經過數據流和控制流分析,肯定程序語義是合法的、符合邏輯的。
  4. 符號引用驗證。這一階段的校驗發生在虛擬機將符號引用轉化爲直接引用的時候,這個轉化動做將在鏈接的第三個階段-解析階段中發生。符號引用驗證能夠看作是對類自身之外(常量池中的各類符號引用)的信息進行匹配性校驗。

準備階段

準備階段是正式爲類變量分配內存並設置類變量初始值的階段,這些變量所使用的的內存都將在方法區中進行分配。首先,這時候進行內存分配的僅包括類變量(被 static 修飾的變量),而不包括實例變量,實例變量將會在對象實例化時隨着對象一塊兒分配在 Java 堆中。下圖是 Java 基本數據類型的初始值:3d

基本數據類型的初始值

解析階段

解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程。code

初始化

類初始化階段是類加載過程的最後一步,前面的類加載過程當中,除了在加載階段用戶應用程序能夠經過自定義類加載器參與以外,其他動做徹底由虛擬機主導和控制。到了初始化階段,才真正開始執行類中定義的 Java 程序代碼。cdn

什麼是雙親委派

從 Java 虛擬機的角度來講,只存在兩種不一樣的類加載器。對象

  1. 一種是啓動類加載器 Bootstrap ClassLoader。該類加載器使用 C++ 語言實現,屬於虛擬機自身的一部分。
  2. 一種就是全部其它的類加載器。這些類加載器是由 Java 語言實現,獨立於虛擬機外部,而且所有繼承自抽象類 java.lang.ClassLoader

從 Java 開發人員的角度來看,大部分 Java 程序通常會使用到如下三種系統提供的類加載器:blog

  1. 啓動類加載器 Bootstrap ClassLoader。負責加載 <JAVA_HOME>\lib 目錄中而且能被虛擬機識別的類庫到虛擬機內存中,若是名稱不符合的類庫即便放在 lib 目錄中也不會被加載。該類加載器沒法被 Java 程序直接引用
  2. 擴展類加載器 Extension ClassLoader。該加載器主要是負責加載 <JAVA_HOME>\lib\ext,該加載器能夠被開發者直接使用
  3. 應用程序類加載器 Application ClassLoader。該類加載器也稱爲系統類加載器,它負責加載用戶類路徑 CLASS_PATH 上所指定的類庫,開發者能夠直接使用該類加載器,若是應用程序中沒有自定義過本身的類加載器,通常狀況下這個就是程序中默認的類加載器

下圖展現了類加載器之間的這種層次關係。這種模型被稱爲類加載器的 雙親委派模型

雙親委派模型

雙親委派模型的工做過程是:若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載請求都最終應該傳送到頂層的啓動類加載器中,只有當父類加載器反饋本身沒法完成這個加載請求(它的搜索範圍中沒有找到所需的類)時,子類加載器纔會嘗試本身去加載。

爲何這麼設計?

使用 雙親委派模型 來組織類加載器之間的關係,有一個顯而易見的好處就是 Java 類隨着它的類加載器一塊兒具有了一種帶有優先級的層次關係。例如類 java.lang.Object,它存放在 rt.jar 之中,不管哪個類加載器要加載這個類,最終都是委派給處於模型最頂端的啓動類加載器進行加載,所以 Object 類在程序的各類類加載器環境中都是同一個類。相反,若是沒有使用雙親委派模型,由各個類自行去加載的話,若是用戶本身編寫了一個 java.lang.Object 的類,並放在程序的 CLASS_PATH 中,那系統中將會出現多個不一樣的 Object 類,Java 體系同最基礎的行爲也就沒法保證,應用程序也會變得一片混亂。

相關文章
相關標籤/搜索