【學習筆記】—JVM(四)虛擬機類加載機制

一、什麼是類加載機制

虛擬機把描述類的數據從Class文件加載到內存,並對數據進行檢驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型。

二、類的生命週期

在這裏插入圖片描述
  加載、驗證、準備、初始化和卸載這5個順序是固定的,按這樣的順序開始,但是解析不一定,爲了支持動態綁定的緣故

觸發初始化的有且只有的5種場景(主動引用)

    1. 遇到new,getstatic,putstatic,invokstatic,指令時,若這個類沒有初始化則進行初始化。
    2. 使用java.lang.reflect包的方法進行反射時。
    3. 初始化一個類時,其父類還沒有初始化過。對其父類初始化。
    4. 指定一個要執行的主類(包含main()方法的類)時。
    5.使用動態語言支持時,最後解析的結果是REF_getstatic,REF_putstatic,REF_invokstatic。

  其他的引用都稱之爲被動引用,例如,子類引用父類的靜態字段,子類不會初始化。引用定義的常量,因爲常量在編譯階段會存入調用類的常量池,引用後沒有直接引用到定義的常量,故不會初始化。

  接口和類差不多,但是類初始化時要求其父類全部初始化,而接口則是在真正用到父類接口時再初始化

三、類加載過程

加載

  非數組類的加載既可以使用系統提供的引導類加載器完成,也可以用戶自定義。而數組類是由Java虛擬機直接創建,但是元素類型需要加載器創建。

  加載階段完成後,虛擬機外部的二進制字節流就按照虛擬機所需要的格式存儲在方法區之中。
  加載和連接階段是交替進行的

驗證

  爲了確保Class文件得字節流中包含的信息符合當前虛擬機的要求。大致分爲:文件格式驗證,元數據驗證,字節碼驗證,符號引用驗證

  文件格式驗證:檢驗魔數,版本號等等, 主要目的是擺正字節流能正確的解析並存儲與方法區之內。這個階段是基於二進制字節流進行的。後面的3個階段都是基於方法區的存儲結構來進行的,不會再直接操作字節流
  元數據驗證:對信息進行語義分析是否符合Java語言規範。
  字節碼驗證:通過數據流和控制流分析,確定程序語義是合法的,符合邏輯的。但一個方法通過了字節碼驗證,也不能說他一定就是安全的
  符號引用驗證:發生在虛擬機將符號引用轉化爲直接引用的時候。這個階段非常重要但不必要,對那些反覆使用和驗證過的代碼,可以設置不驗證來縮短類加載的時間

準備

  正式爲類變量分配內存並設置初值

解析

  將常量池內的符號引用替換爲直接應用的過程。主要分爲類或接口的解析,字段解析,類方法解析,接口方法解析

初始化

  在初始化階段,可根據程序員通過程序制定的主觀計劃去初始化類變量和其他資源,也可說初始化階段是執行類構造器<clinet>()方法

虛擬機會保證子類的<clinet>()方法執行之前,父類的<clinet>()方法已經執行完畢,因此虛擬機中第一個被執行的<clinet>()方法的類肯定是Object,且意味着父類中定義的靜態語句塊要優先於子類的變量賦值操作

  靜態語句塊中只能訪問到定義在靜態語句塊之前的變量,定義在他之後的變量,在他前面的靜態語句塊中可以賦值,但不能訪問

pubic class Test{
	static {
		i = 0;//可以賦值
		System.out.print(i);//報錯,非法向前引用
	}
	static int i =1;
}

  如果多個線程同時去初始化一個類,那麼只會有一個線程去執行<clinet>()其他的都要阻塞等待,顯然會引起多個進程阻塞。

類加載器

通過一個類的全限定名來獲取此類的二進制字節流,實現這個動作的的代碼模塊叫做類加載器。

  只用於實現類的加載動作。
  對於任何一個類都需要由加載他的類加載器和本身確立其唯一性。兩個類來源於同一個Class文件,被同一個虛擬機加載只要他們的類加載器不同,那麼他們絕對就不相等

四、雙親委派模型

爲什麼要雙親委派模型

  上面我們說到類相不相等的問題,爲了保證相同的class文件,在使用的時候,是相同的對象
  工作過程

如果一個類加載器收到了加載某個類的請求,則該類加載器並不會去加載該類,而是把這個請求委派給父類加載器,每一個層次的類加載器都是如此,因此所有的類加載請求最終都會傳送到頂端的啓動類加載器;只有當父類加載器在其搜索範圍內無法找到所需的類,並將該結果反饋給子類加載器,子類加載器會嘗試去自己加載。

從虛擬機角度來說只有兩種類加載器,啓動類加載器,是虛擬機自身的一部分。另一種就是所有其他類加載器,獨立於虛擬機外部。
從開發人員的角度來看則是分爲
  啓動類加載器: 管理最核心的類例如:String。無法通過getClassLoader()獲得ClassLoader名爲null
  擴展類加載器
  應用程序加載器(默認加載器)
在這裏插入圖片描述
  Java類隨着類加載器一起具備了帶有優先級的層次關係(雖然叫父加載器,引用指向,但非繼承關係,能通過getParent()獲取引用。)。例如Oject不同的類加載器要加載這個類是,都要找到最頂端的啓動類加載器。所以始終都是同一個Object,而如果然每個類加載器自己加載Object的話就會產生很多個Oject,因爲類加載不同他們不能相等不能算作一個,就會產生混亂。

破壞雙親委派模型

  在某些情況下父類加載器需要委託子類加載器去加載class文件。更具前面的雙親工作過程,父類加載器無法加載到需要的文件,所以要破壞它

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章