JVM_02 類加載子系統

完整JVM學習筆記請戳

JVM架構圖

1.類加載子系統做用

  • 類加載子系統負責從文件系統或者網絡中加載Class文件,class文件在文件開頭有特定的文件標識;
  • ClassLoader只負責class文件的加載,至於它是否能夠運行,則由Execution Engine決定
  • 加載的類信息存放於一塊成爲方法區的內存空間。除了類信息以外,方法區還會存放運行時常量池信息,可能還包括字符串字面量和數字常量(這部分常量信息是Class文件中常量池部分的內存映射)

1.1類加載器ClassLoader角色

1.2加載

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

1.3 連接

1.3.1 驗證:

  • 目的在於確保Class文件的字節流中包含信息符合當前虛擬機要求,保證被加載類的正確性,不會危害虛擬機自身安全。
  • 主要包括四種驗證,文件格式驗證,源數據驗證,字節碼驗證,符號引用驗證。

1.3.2 準備

  • 爲類變量分配內存而且設置該類變量的默認初始值,即零值;
  • 這裏不包含用final修飾的sttic,由於final在編譯的時候就會分配了,準備階段會顯式初始化;
  • 之類不會爲實例變量分配初始化,類變量會分配在方法去中,而實例變量是會隨着對象一塊兒分配到java堆中。

1.3.3 解析

  • 將常量池內的符號引用轉換爲直接引用的過程。
  • 事實上,解析操做網晚會伴隨着jvm在執行完初始化以後再執行
  • 符號引用就是一組符號來描述所引用的目標。符號應用的字面量形式明肯定義在《java虛擬機規範》的class文件格式中。直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄
  • 解析動做主要針對類或接口、字段、類方法、接口方法、方法類型等。對應常量池中的CONSTANT_Class_info/CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。

1.4初始化

  • 初始化階段就是執行類構造器方法clinit()的過程。
  • 此方法不須要定義,是javac編譯器自動收集類中的全部類變量的賦值動做和靜態代碼塊中的語句合併而來。 咱們注意到若是沒有靜態變量c,那麼字節碼文件中就不會有clinit方法
  • 構造器方法中指令按語句在源文件中出現的順序執行
  • clinit()不一樣於類的構造器。(關聯:構造器是虛擬機視角下的init())
  • 若該類具備父類,jvm會保證子類的clinit()執行前,父類的clinit()已經執行完畢
  • 虛擬機必須保證一個類的clinit()方法在多線程下被同步加鎖。

2.類加載器分類

  • JVM支持兩種類型的加載器,分別爲引導類加載器(BootStrap ClassLoader)自定義類加載器(User-Defined ClassLoader)
  • 從概念上來說,自定義類加載器通常指的是程序中由開發人員自定義的一類類加載器,可是java虛擬機規範卻沒有這麼定義,而是將全部派生於抽象類ClassLoader的類加載器都劃分爲自定義類加載器
  • 不管類加載器的類型如何劃分,在程序中咱們最多見的類加載器始終只有三個,以下所示:

2.1 自定義類與核心類庫的加載器

  • 對於用戶自定義類來講:使用系統類加載器AppClassLoader進行加載
  • java核心類庫都是使用引導類加載器BootStrapClassLoader加載的
/**
 * ClassLoader加載
 */
public class ClassLoaderTest {
    public static void main(String[] args) {
        //獲取系統類加載器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //獲取其上層  擴展類加載器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@610455d6

        //獲取其上層 獲取不到引導類加載器
        ClassLoader bootStrapClassLoader = extClassLoader.getParent();
        System.out.println(bootStrapClassLoader);//null

        //對於用戶自定義類來講:使用系統類加載器進行加載
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //String 類使用引導類加載器進行加載的  -->java核心類庫都是使用引導類加載器加載的
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1);//null

    }
}
複製代碼

2.2 虛擬機自帶的加載器

  • ①啓動類加載器(引導類加載器,BootStrap ClassLoader)
    • 這個類加載使用C/C++語言實現的,嵌套在JVM內部
    • 它用來加載java的核心庫(JAVA_HOME/jre/lib/rt.jar/resources.jar或sun.boot.class.path路徑下的內容),用於提供JVM自身須要的類
    • 並不繼承自java.lang.ClassLoader,沒有父加載器
    • 加載拓展類和應用程序類加載器,並指定爲他們的父加載器
    • 處於安全考慮,BootStrap啓動類加載器只加載包名爲java、javax、sun等開頭的類
  • ②拓展類加載器(Extension ClassLoader)
    • java語言編寫 ,由sun.misc.Launcher$ExtClassLoader實現。
    • 派生於ClassLoader類
    • 父類加載器爲啓動類加載器
    • 從java.ext.dirs系統屬性所指定的目錄中加載類庫,或從JDK的安裝目錄的jre/lib/ext子目錄(擴展目錄)下加載類庫。若是用戶建立的JAR放在此目錄下,也會由拓展類加載器自動加載
  • ③應用程序類加載器(系統類加載器,AppClassLoader)
    • java語言編寫, 由sun.misc.Launcher$AppClassLoader實現。
    • 派生於ClassLoader類
    • 父類加載器爲拓展類加載器
    • 它負責加載環境變量classpath或系統屬性 java.class.path指定路徑下的類庫
    • 該類加載器是程序中默認的類加載器,通常來講,java應用的類都是由它來完成加載
    • 經過ClassLoader#getSystemClassLoader()方法能夠獲取到該類加載器
    代碼演示
/**
 * 虛擬機自帶加載器
 */
public class ClassLoaderTest1 {
    public static void main(String[] args) {
        System.out.println("********啓動類加載器*********");
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        //獲取BootStrapClassLoader可以加載的api路徑
        for (URL e:urls){
            System.out.println(e.toExternalForm());
        }

        //從上面的路徑中隨意選擇一個類 看看他的類加載器是什麼
        //Provider位於 /jdk1.8.0_171.jdk/Contents/Home/jre/lib/jsse.jar 下,引導類加載器加載它
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println(classLoader);//null

        System.out.println("********拓展類加載器********");
        String extDirs = System.getProperty("java.ext.dirs");
        for (String path : extDirs.split(";")){
            System.out.println(path);
        }
        //從上面的路徑中隨意選擇一個類 看看他的類加載器是什麼:拓展類加載器
        ClassLoader classLoader1 = CurveDB.class.getClassLoader();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@4dc63996
    }
}
複製代碼

2.3 用戶自定義類加載器

爲何html

  • 隔離加載類
  • 修改類加載的方式
  • 拓展加載源
  • 防止源碼泄漏

3 ClassLoader的經常使用方法及獲取方法

3.1 ClassLoader類,它是一個抽象類,其後全部的類加載器都繼承自ClassLoader(不包括啓動類加載器)

方法名稱 描述
getParent() 返回該類加載器的超類加載器
loadClass(String name) 加載名稱爲name的類,返回結果爲java.lang.Class類的實例
findClass(String name) 查找名稱爲name的類,返回結果爲java.lang.Class類的實例
findLoadedClass(String name) 查找名稱爲name的已經被加載過的類,返回結果爲java.lang.Class類的實例
defineClass(String name,byte[] b,int off,int len) 把字節數組b中的內容轉換爲一個Java類 ,返回結果爲java.lang.Class類的實例
resolveClass(Class<?> c) 鏈接指定的一個java類

3.2 ClassLoader繼承關係

拓展類加載器和系統類加載器間接繼承於ClassLoader抽象類java

3.3 獲取ClassLoader的途徑

4. 雙親委派機制

Java虛擬機對class文件採用的是按需加載的方式,也就是說當須要使用該類時纔會將她的class文件加載到內存生成的class對象。並且加載某個類的class文件時,java虛擬機採用的是雙親微拍模式,即把請求交由父類處理,它是一種任務委派 模式git

4.1 雙親委派機制工做原理


如圖,雖然咱們自定義了一個java.lang包下的String嘗試覆蓋核心類庫中的String,可是因爲雙親委派機制,啓動加載器會加載java核心類庫的String類(BootStrap啓動類加載器只加載包名爲java、javax、sun等開頭的類),而核心類庫中的String並無main方法

4.2 雙親委派機制的優點

  • 避免類的重複加載
  • 保護程序安全,防止核心API被隨意篡改
    • 自定義類:java.lang.String
    • 自定義類:java.lang.MeDsh(java.lang包須要訪問權限,阻止咱們用包名自定義類)

5. 沙箱安全機制

自定義String類,可是在加載子弟敬意String類的時候回率先使用引導類加載器加載,而引導類加載器在加載過程當中會先加載jdk自帶的文件(rt.jar包中的java\lang\String.class),報錯信息說沒有main方法就是由於加載的是rt.jar包中的String類。這樣能夠保證對java核心源代碼的保護,這就是沙箱安全機制.github

類比舉例: 咱們在讀寫U盤信息時能夠用360沙箱,防止U盤內的病毒等對沙箱外的系統構成污染算法

6.其餘

  • 在jvm中表示兩個class對象是否爲同一個類存在的兩個必要條件
    • 類的完整類名必須一致,包括包名
    • 加載這個類的ClassLoader(指ClassLoader實例對象)必須相同
  • 換句話說,在jvm中,即便這兩個類對象(class對象)來源同一個Class文件,被同一個虛擬機所加載,但只要加載它們的ClassLoader實例對象不一樣,那麼這兩個類對象也是不相等的.

對類加載器的引用

JVM必須知道一個類型是有啓動類加載器加載的仍是由用戶類加載器加載的。若是一個類型由用戶類加載器加載的,那麼jvm會將這個類加載器的一個引用做爲類型信息的會議部分保存在方法區中。當解析一個類型到另外一個類型的引用的時候,JVM須要保證兩個類型的加載器是相同的。api

類的主動使用和被動使用

java程序對類的使用方式分爲:主動使用和被動使用數組

  • 主動使用,分爲七種狀況
    • 建立類的實例
    • 訪問某各種或接口的靜態變量,或者對靜態變量賦值
    • 調用類的靜態方法
    • 反射 好比Class.forName(com.dsh.jvm.xxx)
    • 初始化一個類的子類
    • java虛擬機啓動時被標明爲啓動類的類
    • JDK 7 開始提供的動態語言支持:
      java.lang.invoke.MethodHandle實例的解析結果REF_getStatic、REF_putStatic、REF_invokeStatic句柄對應的類沒有初始化,則初始化
  • 除了以上七種狀況,其餘使用java類的方式都被看做是對類的被動使用,都不會致使類的初始化。


JVM學習代碼及筆記(陸續更新中...)

【代碼】
github.com/willShuhuan…
【筆記】
JVM_01 簡介
JVM_02 類加載子系統
JVM_03 運行時數據區1- [程序計數器+虛擬機棧+本地方法棧]
JVM_04 本地方法接口
JVM_05 運行時數據區2-堆
JVM_06 運行時數據區3-方法區
JVM_07 運行時數據區4-對象的實例化內存佈局與訪問定位+直接內存
JVM_08 執行引擎(Execution Engine)
JVM_09 字符串常量池StringTable
JVM_10 垃圾回收1-概述+相關算法
JVM_11 垃圾回收2-垃圾回收相關概念
JVM_12 垃圾回收3-垃圾回收器安全

相關文章
相關標籤/搜索