Java虛擬機--類加載機制

1.什麼是類加載機制java

  虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終造成能夠被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。程序員

2.類的加載時機安全

            

  圖中的七個階段表明這類的生命週期,其中加載驗證準備初始化卸載的順序是肯定的,按這種順序循序漸進的「開始」,而解析則不必定:它在某些狀況下能夠在初始化階段以後再開始,這是爲了支持Java語言的運行時綁定。另外要注意:剛纔的5個階段一般都是相互交叉地混合式進行,一般會在一個階段執行的過程當中調用、激活另一個階段。數據結構

  加載階段什麼時候開始沒有作強制約束,但對於初始化階段,虛擬機規範則規定了有且只有5種狀況必須對類進行「初始化」:  函數

  ■ 遇到new、getstatic、putstatic或invokestatic這4條字節碼指令時。觸發場景:new關鍵字實例化對象、讀取或設置一個類靜態字段(被final修飾、已在編譯器放入常量池的除外)、調用另外一個類的靜態方法。this

  ■ 使用java.lang.reflect包的方法對類進行反射調用時。spa

  ■ 初始化一個類時,若是發現其父類還沒初始化,就要先初始化父類。指針

  ■ JVM啓動時,用戶指定一個可執行的主類,先初始化這個主類。code

  ■ 當使用JDK 1.7動態語言支持時,若是一個java.lang.invoke.MethodHandle實例最後的解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,切方法句柄所對應的類沒有初始化時。對象

3.類的加載過程

  加載

  加載階段JVM完成件事:

  ■ 經過類的全限定名來獲取定義此類的二進制數據流。(若是沒有找到對應類文件,只有在類實際使用時才拋出錯誤)  

  ■ 將數據流分析並轉化爲方法區(JVM對運行數據區的劃分結構,包括堆、棧、方法區等)的運行時數據結構。另外,這裏處理了部分檢驗,如對類文件的魔數的驗證、檢查文件是否過長或太短、肯定是否有父類。

  ■ 在內存中生成一個表明這個類的java.lang.class對象,做爲方法區這個類的各類數據的訪問入口。

  鏈接

  鏈接指的是將Java類的二進制代碼合併到JVM的運行狀態之中的過程,鏈接以前必須被成功加載。鏈接分三個步驟:

  ■ 驗證:用來確保Java類的二進制表示在結構是是徹底正確的、符合虛擬機規範的。如文件格式驗證(是否以魔數0xCAFEBABE開頭、主次版本號是都被虛擬機支持、常量類型是否都被支持)、元數據驗證(就是語義分析,如是否有父類、這個類的父類是否繼承了不被容許的類等)、字節碼驗證(經過數據流和控制流分析,肯定程序語義是合法的)、符號引用驗證(將符號引用轉化爲直接引用,發生在解析階段),若是驗證出現錯誤的話,會拋出java.lang.verifyError錯誤異常。

  ■ 準備:正式爲類變量(被static修飾)分配內存並設置類變量初始值(數據類型的零值)的階段,這些變量所使用的內存都將在方法區(持久代)中進行分配。

  ■ 解析:將常量池內的符號引用替換爲直接引用的過程,目的是確保這些被引用的類能被正確的找到。符號引用:以一組符號來描述引用的目標,符號能夠是任意無歧義的字面量。直接引用:能夠是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。

  初始化

  類的初始化是延遲的,直到第一次被主動使用(active use),JVM纔會初始化類。初始化過程的主要操做是執行靜態代碼塊和初始化靜態域,在一個類被初始化以前,它的直接父類也要被初始化,可是一個接口的初始化不會引發其父接口的初始化。

4.類加載的推進者--類加載器

  Java中的全部類,必須被加載到JVM中才能運行,這個加載的工做是由JVM中的類加載器完成,類加載器的實質是把類文件從硬盤讀取到內存中,JVM加載類時都是經過ClassLoaderloadClass()方法,此方法使用了雙親委派模式

  接下來咱們看loadClass方法中雙親委派的實現:

    

  圖中有一個同步代碼塊synchronized (getClassLoadingLock(name)),咱們來看getClassLoadingLock(name)的做用是什麼:

    

  咱們這裏看到parallelLockMap變量,根據這個變量進行不一樣的操做,若是變量是null,直接返回this;若是不爲null,就新建一個對象,調用putIfAbsent方法給剛建好的對象賦值,那這個變量的來源是哪?

    

  咱們發現這個變量是ClassLoader類的成員變量,這個變量的初始化工做在ClassLoader的構造函數中:

    

  這裏咱們能夠看到構造函數根據一個屬性ParallelLoadersRegistered狀態的不一樣來給parallelLockMap 賦值。 那ParallelLoaders又來自何方呢?

    

  咱們發現,在ClassLoader類中包含一個靜態內部類private static class ParallelLoaders,在ClassLoader被加載的時候這個靜態內部類就被初始化。這個類的意思就是:封裝了並行的可裝載的類型的集合

  綜上源碼,getClassLoadingLock(String className)的做用:爲類的加載操做返回一個鎖對象。爲了向後兼容,若是當前的classloader對象註冊了並行能力,方法返回一個與指定的名字className相關聯的特定對象,不然,直接返回當前的classloader對象。咱們看到,classloader源碼中還有一個resolveClass方法,它的做用是:連接指定的類,這個方法給classloader用來連接一個類,若是這個類已經被連接過了,那麼這個方法只作一個簡單的返回;不然,這個類將被按照Java™規範中的Execution描述進行連接。

5.類加載器總結

  java中的類大體分三種:系統類、擴展類、程序員自定義的類。

  類加載方式有兩種:隱式加載,程序在運行過程當中碰到new關鍵字等方式生成對象時,隱式調用類加載器;顯式加載,經過Class.forName()等方法,顯式加載類。

  類加載的動態性體現:當運行一個由n多類組成的應用程序時,它會先把保證程序運行的基礎類一次性加載入JVM中,其餘類當用到時再進行加載,這樣節省了內存開銷。

  Java類加載器:加載器的實質也是類,功能是把類加載入JVM,但JVM的加載器有三個,一方面是是各自負責各自的區塊,另外一方面實現委託模型。其層次結構:

        

  類加載器之間協調工做:那當咱們須要加載一個類時,是由哪個加載器來加載的呢?在這裏Java採用委託模型機制,這個機制就是「類加載器有載入類的需求時,會先請示其Parent使用其搜索路徑幫忙載入,若是Parent找不到,再依照本身的搜索路徑搜索類」。好比咱們本地有個Test類,調用Test.class.getClassLoader()時得到的加載器爲AppClassLoaderAppClassLoader的父加載器爲ExtClassLoaderExtClassLoader的父加載器爲根加載器,但在IDE控制檯打印時爲null,是由於它由C++編寫,在Java中看不到它。默認狀況下,採用AppClassLoader加載程序類

  採用「委託機制」是從安全考慮的。試想,若是自定義了一個"java.lang.string"的惡意類,且其被加載入JVM中,會引發嚴重的後果。而採用「委託機制」,這個類永遠只會由跟加載器加載。

相關文章
相關標籤/搜索