01.JVM-類加載

類的加載流程

image

類的加載流程包含:加載、驗證、準備、解析、初始化java

加載

做爲第一個階段,虛擬機須要完成了之後三個步驟緩存

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

驗證

確保被加載類的正確性,包含檢測如下信息安全

  1. 文件格式的驗證:驗證字節流是否符合Class文件格式的規範
  2. 元數據的驗證::對字節碼描述的信息進行語義分析
  3. 字節碼的驗證:經過數據流和控制流分析,肯定程序語義是合法的、符合邏輯的。
  4. 符號引用驗證:確保解析動做能正確執行。

準備

爲類的靜態變量分配內存,並將其初始化默認值bash

  • 這個時候進行內存分配的僅是類變量(static),而不包括實例變量,實例變量會在對象實例化的時候,隨着對象一塊分配到堆中
  • 這的初始化默認值,一般狀況下是數據類型默認的零值(如0、0L、null、false等),而不是指的是顯示賦值

解析

將類中的符號引用轉換爲直接引用網絡

解析階段就是虛擬機將常量池內符號引用替換爲直接引用,解析的動做主要是針對類的接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定符7類符號引用進行。數據結構

  • 符號引用與虛擬機實現的佈局無關,引用的目標並不必定要已經加載到內存中。各類虛擬機實現的內存佈局能夠各不相同,可是它們能接受的符號引用必須是一致的,由於符號引用的字面量形式明肯定義在Java虛擬機規範的Class文件格式中。
  • 直接引用能夠是指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄。若是有了直接引用,那引用的目標一定已經在內存中存在。

初始化

爲類的靜態變量賦予正確的初始值,到了初始階段,纔開始真正執行類中定義的Java程序代碼。jvm

  • 聲明類變量是指定初始值
  • 使用靜態代碼塊爲類變量指定初始值

類的加載器

示例圖佈局

image

==注意:這裏父類加載器並非經過繼承關係來實現的,而是採用組合實現的==加密

在java虛擬機的角度來講只存在兩種不一樣的類加載器spa

  • 啓動類的加載器:使用C++實現
  • 全部其餘類的加載器:使用java語言實現的,獨立於虛擬機以外,而且所有繼承自抽象類java.lang.Classlocad,這些類的加載器須要由啓動類加載器加載到內存中以後才能去加載其餘類

在開發者的角度來講,類的加載器能夠大體劃分爲三類

  • 啓動類加載器
  • 擴展類加載器
  • 應用程序類加載器

類加載器的加載機制

  • 全盤負責:當一個類加載器負責加載某Class時候,該Class依賴的Class和引用的Classs也將由該類加載器負責載入,除非顯示的使用另外一個類加載器來載入
  • 父類委託:先讓父類加載器試圖加載該類,只有在父類加載器沒法加載該類的時候,才嘗試從本身的類路徑中加載該類
  • 緩存機制:緩存機制將保證全部加載過的Class都會被緩存,當程序中須要使用某一個Class,類加載器會如今緩存中查找該Class,只要緩存不存在的時候,系統纔會讀取該類對應的二進制數據,將其轉化成Class對象,存入緩存區。這就是爲何修改了Class後,必須重啓JVM,程序的修改纔會生效

類的加載

類加載的方式

  • 命令行啓動應用時候由JVM初始化加載
  • 經過Class.forName()方法動態加載:將類的.class文件加載到jvm中,並會對類進行解釋,執行類的static塊
  • 經過ClassLoader.loadClass()方法動態加載:只作一件事,將類的.class文件加載到jvm中,不會執行static塊,只有在newInstance纔會去執行static塊

雙親委派機制

雙親委派機制的流程:若是一個類加載器收到了一個類加載的請求,首先他不會嘗試本身加載該類,而是把這個請求委託給父類加載器加載該類,依次向上傳遞,所以,全部的類記載請求都會請求到啓動類加載器中,父類加載器在他的搜索範圍內沒有找到所須要的類時,即沒法完成該類的加載,子加載器纔會嘗試本身去加載該類,若是尚未找到,那麼直接拋出異常

示例流程詳解

    1. AppClassLoader加載一個class時,它首先不會本身去嘗試加載這個類,而是把類加載請求委派給父類加載器ExtClassLoader去完成。
    1. ExtClassLoader加載一個class時,它首先也不會本身去嘗試加載這個類,而是把類加載請求委派給BootStrapClassLoader去完成。
    1. 若是 BootStrapClassLoader加載失敗(例如在 $JAVA_HOME/jre/lib裏未查找到該class),會使用 ExtClassLoader來嘗試加該類
    1. ExtClassLoader也加載失敗,則會使用 AppClassLoader來加載,若是 AppClassLoader也加載失敗,則會報出異常 ClassNotFoundException。

雙親委派機制的意義

  • 系統類防止內存中出現多份一樣的字節碼
  • 保證Java程序安全穩定運行

代碼示例

// 首先判斷該類型是否已經被加載
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                //若是沒有被加載,就委託給父類加載或者委派給啓動類加載器加載
                    if (parent != null) {
                     
            //若是存在父類加載器,就委派給父類加載器加載
                        c = parent.loadClass(name, false);
                    } else {
                    //若是不存在父類加載器,就檢查是不是由啓動類加載器加載的類,經過調用本地方法native Class findBootstrapClass(String name)
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
複製代碼

自定義的類加載器

一般狀況下,咱們都是直接使用系統類加載器。可是,有的時候,咱們也須要自定義類加載器。好比應用是經過網絡來傳輸 Java類的字節碼,爲保證安全性,這些字節碼通過了加密處理,這時系統類加載器就沒法對其進行加載,這樣則須要自定義類加載器來實現。自定義類加載器通常都是繼承自 ClassLoader類,從上面對 loadClass方法來分析來看,咱們只須要重寫 findClass 方法便可。

自定義類加載器的核心在於對字節碼文件的獲取,若是是加密的字節碼則須要在該類中對文件進行解密

相關文章
相關標籤/搜索