要點提煉| 理解JVM之類載入機制
本篇將瞭解類載入機制和雙親委派模型這兩大知識考點:
- 概述
- 類載入全過程
- 類載入器&雙親委派模型
1.概述
a.JVM類載入機制:是虛擬機器把描述類的資料從Class
檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可被虛擬機器直接使用的Java
型別的過程。
b.特性:執行期類載入。即在Java語言裡面,型別的載入、連線和初始化過程都是在程式執行期完成的,從而通過犧牲一些效能開銷來換取Java程式的高度靈活性。
JVM執行期動態載入+動態連線->Java語言的動態擴充套件特性
2.類載入全過程
類從被載入到虛擬機器記憶體中開始、到卸載出記憶體為止,整個生命週期包括7階段:
- 載入(Loading)
- 驗證(Verification)
- 準備(Preparation)
- 解析(Resolution)
- 初始化(Initialization)
- 使用(Using)
- 解除安裝(Unloading)
其中,驗證、準備、解析這3個部分統稱為連線(Linking),流程如下圖。
注意:
- 『載入』->『驗證』->『準備』->『初始化』->『解除安裝』這5個階段的順序是確定的,而『解析』可能為了支援Java語言的執行時繫結會在『初始化』後才開始。
- 上述階段通常都是互相交叉地混合式進行的,比如會在一個階段執行的過程中呼叫、啟用另外一個階段。
接下來將分別介紹上述幾個階段。
a.載入
任務:
- 通過類的全限定名來獲取定義此類的二進位制位元組流。如從ZIP包讀取、從網路中獲取、通過執行時計算生成、由其他檔案生成、從資料庫中讀取等等途徑……
- 將該二進位制位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構,該資料儲存資料結構由虛擬機器實現自行定義。
- 在記憶體中生成一個代表這個類的
java.lang.Class
物件,它將作為程式訪問方法區中的這些型別資料的外部介面。
b.驗證
- 是連線階段的第一步,且工作量在JVM類載入子系統中佔了相當大的一部分。
- 目的:為了確保Class檔案的位元組流中包含的資訊符合當前虛擬機器的要求,並且不會危害虛擬機器自身的安全。
由此可見,它能直接決定JVM能否承受惡意程式碼的攻擊,因此驗證階段很有必要,但由於它對程式執行期沒有影響,並不一定必要,可以考慮使用
-Xverify:none
引數來關閉大部分的類驗證措施,以縮短虛擬機器類載入的時間。
- 檢驗過程包括以下四個階段:
- 檔案格式驗證:
- 內容:驗證位元組流是否符合Class檔案格式的規範、以及是否能被當前版本的虛擬機器處理。
- 目的:保證輸入的位元組流能正確地解析並存儲於方法區之內,且格式上符合描述一個Java型別資訊的要求。只有保證二進位制位元組流通過了該驗證後,它才會進入記憶體的方法區中進行儲存,所以後續3個驗證階段全部是基於方法區而不是位元組流了。
- 元資料驗證:
- 內容:對位元組碼描述的資訊進行語義分析,以保證其描述的資訊符合Java語言規範的要求。
- 目的:對類的元資料資訊進行語義校驗,保證不存在不符合Java語言規範的元資料資訊。
- 位元組碼驗證:是驗證過程中最複雜的一個階段。
- 內容:對類的方法體進行校驗分析,保證被校驗類的方法在執行時不會做出危害虛擬機器安全的事件。
- 目的:通過資料流和控制流分析,確定程式語義是合法的、符合邏輯的。
- 符號引用驗證:
- 內容:對類自身以外(如常量池中的各種符號引用)的資訊進行匹配性校驗。
- 目的是確保解析動作能正常執行,如果無法通過符號引用驗證,那麼將會丟擲一個
java.lang.IncompatibleClassChangeError
異常的子類。 - 注意:該驗證發生在虛擬機器將符號引用轉化為直接引用的時候,即『解析』階段。
- 檔案格式驗證:
c.準備
任務:
- 為類變數分配記憶體:因為這裡的變數是由方法區分配記憶體的,所以僅包括類變數而不包括例項變數,後者將會在物件例項化時隨著物件一起分配在Java堆中。
- 設定類變數初始值:通常情況下零值。
d.解析
- 之前提過,解析階段就是虛擬機器將常量池內的符號引用替換為直接引用的過程。
- 符號引用(Symbolic References):以一組符號來描述所引用的目標。
- 可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。
- 與虛擬機器實現的記憶體佈局無關,因為符號引用的字面量形式明確定義在Java虛擬機器規範的Class檔案格式中,所以即使各種虛擬機器實現的記憶體佈局不同,但是能接受符號引用都是一致的。
- 直接引用(Direct References):
- 可以是直接指向目標的指標、相對偏移量或是一個能間接定位到目標的控制代碼。
- 與虛擬機器實現的記憶體佈局相關,同一個符號引用在不同虛擬機器例項上翻譯出來的直接引用一般不同。
- 符號引用(Symbolic References):以一組符號來描述所引用的目標。
- 發生時間:JVM會根據需要來判斷,是在類被載入器載入時就對常量池中的符號引用進行解析,還是等到一個符號引用將要被使用前才去解析。
- 解析動作:有7類符號及其對應在常量池的7種常量型別
- 類或介面(
CONSTANT_Class_info
) - 欄位(
CONSTANT_Fieldref_info
) - 類方法(
CONSTANT_Methodref_info
) - 介面方法(
CONSTANT_InterfaceMethodref_info
) - 方法型別(
CONSTANT_MethodType_info
) - 方法控制代碼(
CONSTANT_MethodHandle_info
) - 呼叫點限定符(
CONSTANT_InvokeDynamic_info
)
- 類或介面(
舉個例子,設當前程式碼所處的為類D,把一個從未解析過的符號引用N解析為一個類或介面C的直接引用,解析過程分三步:
- 若C不是陣列型別:JVM將會把代表N的全限定名傳遞給D類載入器去載入這個類C。在載入過程中,由於元資料驗證、位元組碼驗證的需要,又可能觸發其他相關類的載入動作。一旦這個載入過程出現了任何異常,解析過程就宣告失敗。
- 若C是陣列型別且陣列元素型別為物件:JVM也會按照上述規則載入陣列元素型別。
- 若上述步驟無任何異常:此時C在JVM中已成為一個有效的類或介面,但在解析完成前還需進行符號引用驗證,來確認D是否具備對C的訪問許可權。如果發現不具備訪問許可權,將丟擲
java.lang.IllegalAccessError
異常。
e.初始化
- 是類載入過程的最後一步,會開始真正執行類中定義的Java位元組碼。而之前的類載入過程中,除了在『載入』階段使用者應用程式可通過自定義類載入器參與之外,其餘階段均由虛擬機器主導和控制。
- 與『準備』階段的區分:
- 準備階段:變數賦初始零值。
- 初始化階段:根據Java程式的設定去初始化類變數和其他資源,或者說是執行類構造器
<clinit>()
的過程。
<clinit>()
:由編譯器自動收集類中的所有類變數的賦值動作和靜態語句塊static{}
中的語句合併產生。
- 是執行緒安全的,在多執行緒環境中被正確地加鎖、同步。
- 對於類或介面來說是非必需的,如果一個類中沒有靜態語句塊,也沒有對變數的賦值操作,那麼編譯器可以不為這個類生成
<clinit>()
。- 介面與類不同的是,執行介面的
<clinit>()
不需要先執行父介面的<clinit>()
,只有當父介面中定義的變數使用時,父接口才會初始化。另外,介面的實現類在初始化時也一樣不會執行介面的<clinit>()
。
- 在虛擬機器規範中,規定了有且只有五種情況必須立即對類進行『初始化』:
- 遇到
new
、getstatic
、putstatic
或invokestatic
這4條位元組碼指令時; - 使用
java.lang.reflect
包的方法對類進行反射呼叫的時候; - 當初始化一個類的時候,若發現其父類還未進行初始化,需先觸發其父類的初始化;
- 在虛擬機器啟動時,需指定一個要執行的主類,虛擬機器會先初始化它;
- 當使用JDK1.7的動態語言支援時,若一個
java.lang.invoke.MethodHandle
例項最後的解析結果為REF_getStatic
、REF_putStatic
、REF_invokeStatic
的方法控制代碼,且這個方法控制代碼所對應的類未進行初始化,需先觸發其初始化。
- 遇到
3.類載入器&雙親委派模型
每個類載入器都擁有一個獨立的類名稱空間,它不僅用於載入類,還和這個類本身一起作為在JVM中的唯一標識。所以比較兩個類是否相等,只要看它們是否由同一個類載入器載入,即使它們來源於同一個Class檔案且被同一個JVM載入,只要載入它們的類載入器不同,這兩個類就必定不相等。
a.類載入器
從JVM的角度,可將類載入器分為兩種:
- 啟動類載入器(Bootstrap ClassLoader)
- 由
C++
語言實現,是虛擬機器自身的一部分。 - 負責載入存放在
<JAVA_HOME>\lib
目錄中、或被-Xbootclasspath
引數所指定路徑中的、且可被虛擬機器識別的類庫。 - 無法被Java程式直接引用,如果自定義類載入器想要把載入請求委派給引導類載入器的話,可直接用null代替。
- 由
- 其他類載入器:由
Java
語言實現,獨立於虛擬機器外部,並且全都繼承自抽象類java.lang.ClassLoader
,可被Java程式直接引用。常見幾種:
- 擴充套件類載入器(Extension ClassLoader)
- 由
sun.misc.Launcher$ExtClassLoader
實現。 - 負責載入
<JAVA_HOME>\lib\ext
目錄中的、或者被java.ext.dirs
系統變數所指定的路徑中的所有類庫。
- 由
- 應用程式類載入器(Application ClassLoader)
- 是預設的類載入器,是
ClassLoader#getSystemClassLoader()
的返回值,故又稱為系統類載入器。 - 由
sun.misc.Launcher$App-ClassLoader
實現。 - 負責載入使用者類路徑上所指定的類庫。
- 是預設的類載入器,是
- 自定義類載入器(User ClassLoader):如果以上類載入起不能滿足需求,可自定義。
- 擴充套件類載入器(Extension ClassLoader)
上述幾種類載入器的關係如圖:
需要注意的是,雖然陣列類不通過類載入器建立而是由JVM直接建立的,但仍與類載入器有密切關係,因為陣列類的元素型別最終還要靠類載入器去建立。
b.雙親委派模型(Parents Delegation Model)
- 表示類載入器之間的層次關係。
- 前提:除了頂層啟動類載入器外,其餘類載入器都應當有自己的父類載入器,且它們之間關係一般不會以繼承(Inheritance)關係來實現,而是通過組合(Composition)關係來複用父載入器的程式碼。
- 工作過程:若一個類載入器收到了類載入的請求,它先會把這個請求委派給父類載入器,並向上傳遞,最終請求都傳送到頂層的啟動類載入器中。只有當父載入器反饋自己無法完成這個載入請求時,子載入器才會嘗試自己去載入。
- 注意:不是一個強制性的約束模型,而是Java設計者推薦給開發者的一種類載入器實現方式。
- 優點:類會隨著它的類載入器一起具備帶有優先順序的層次關係,可保證Java程式的穩定運作;實現簡單,所有實現程式碼都集中在
java.lang.ClassLoader的loadClass()
中。
比如,某些類載入器要載入
java.lang.Object
類,最終都會委派給最頂端的啟動類載入器去載入,這樣Object
類在程式的各種類載入器環境中都是同一個類。相反,系統中將會出現多個不同的Object類,Java型別體系中最基礎的行為也就無法保證,應用程式也將會變得一片混亂。
相關推薦
要點提煉| 理解JVM之類載入機制
本篇將瞭解類載入機制和雙親委派模型這兩大知識考點: 概述 類載入全過程 類載入器&雙親委派模型 1.概述 a.JVM類載入機制:是虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可被虛擬機器直
深入理解JVM類載入機制
深入理解JVM類載入機制 轉載自:https://blog.csdn.net/a724888/article/details/78396462 簡述:虛擬機器把描述類的資料從class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛
要點提煉| 理解JVM之執行緒安全&鎖優化
本篇將介紹執行緒安全所涉及的概念和分類、同步實現的方式及虛擬機器的底層運作原理,以及虛擬機器為了實現高效併發所採取的一系列鎖優化措施。 概述 執行緒安全 鎖優化 1.概述 在要點提煉| 理解JVM之記憶體模型&執行緒中主要介紹了虛擬機
深入理解JVM一載入機制
人若無名 便可專心練劍 引子 如圖,這是java程式碼到最終執行程式的過程。 1.java程式碼——>靜態編譯(javac)——>byteCode(.class)(通常為靜態編譯,除了特殊情況,如動態代理) 2.Loading——&
理解JVM之類加載機制
反射 fin 規範 賦值 數組 class文件 構造 數據訪問 構造器 類完整的生命周期包括加載,驗證,準備,解析,初始化,使用,卸載,七個階段.其中驗證,準備,解析統稱為連接,類的卸載在前面的關於垃圾回收的博文中已經介紹. 加載,驗證,準備,初始化,卸載這五個階段
理解JVM(四):JVM類載入機制
Class檔案 我們寫的Java程式碼,經過編譯器編譯之後,就成為了.class檔案,從本地機器碼變成了位元組碼。Class檔案是一組以8位位元組為基礎單位的二進位制流,各個資料專案嚴格按照順序緊湊地排列在Class檔案之中,中間沒有新增任何分隔符,這使得整個
《深入理解Java虛擬機器》學習筆記之類載入機制
一、概述 定義: 虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的類載入機制 過程:
深入理解JVM(七)JVM類載入機制
7.1JVM類載入機制 虛擬機器把資料從Class檔案載入到記憶體,並且校驗、轉換解析和初始化最終形成可以被虛擬機器使用的Java型別,這就是虛擬機器的類載入機制。 7.2類載入的時機 1.類載入的步驟開始的順序: 載入(Loading) -> 驗證(Veri
深入探究JVM之類載入與雙親委派機制
@[toc] # 前言 前面學習了虛擬機器的記憶體結構、物件的分配和建立,但物件所對應的類是怎麼載入到虛擬機器中來的呢?載入過程中需要做些什麼?什麼是雙親委派機制以及為什麼要打破雙親委派機制? # 類的生命週期 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020073
深入理解Java類載入機制(一)
1 前言: 在上一篇文章一文讓你明白 Java 位元組碼中, 我們瞭解了java位元組碼的解析過程,那麼在接下來的內容中,我們來了解一下類的載入機制。 2 題外話 Java的核心是什麼?當然是JVM了,所以說了解並熟悉JVM對於我們理解Java語言非常重要,不管你是做Java還是Andr
JVM 類載入機制詳解
原文出處: ziwenxie 如下圖所示,JVM類載入機制分為五個部分:載入,驗證,準備,解析,初始化,下面我們就分別來看一下這五個過程。 載入 載入是類載入過程中的一個階段,這個階段會在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的
JVM類載入機制—結合程式碼分析
《深入理解Java虛擬機器》學習筆記 首先看下面的兩個類 對於被主動引用的類,執行類載入操作,根據給定的全限定名獲取這個類的二進位制位元組流(不一定是class檔案,網路、動態代理等方式也會)並將二進位制按虛擬機器要求的格式儲存在方法區(HotSpot儲存用於訪問方法
JVM 之類載入
一.概述 Java不同於C/C++這類傳統的編譯型語言,也不同於php這一類動態的指令碼語言。可以說Java是一種半編譯語言,我們所寫的類會先被編譯成.class檔案,這個.class是一串二進位制的位元組流。然後當要使用這個類的時候,就會將這個類對應的.cla
JVM(二):JVM類載入機制
如下圖所示,JVM類載入機制分為五個部分:載入,驗證,準備,解析,初始化,下面我們就分別來看一下這五個過程。 載入 載入是類載入過程中的一個階段,這個階段會在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的各種資料的入口。注意這裡不一
[JVM]類載入機制及反射
一、Java類載入機制 1.概述 Class檔案由類裝載器裝載後,在JVM中將形成一份描述Class結構的元資訊物件,通過該元資訊物件可以獲知Class的結構資訊:如建構函式,屬性和方法等,Java允許使用者藉由這個Class相關的元資訊物件間接呼叫Clas
JVM:類載入機制
前言 JAVA虛擬機器中的類載入機制非常重要,在關於JVM的面試中經常問到,因此今天整理一下相關的知識點,一方面為之後的面試做準備,另一個方法也是擴充套件一下知識面。 類的生命週期 關於類的生命週期,直接上圖看下。 類從載入進虛擬機器記憶體,到從虛擬機器解除安裝的整個生命過程包
[jvm]一類載入機制
參考:http://www.cnblogs.com/ityouknow/p/5603287.html java類的載入機制 1、什麼是類的載入 類的載入指的是將類的.class檔案中的二進位制資料讀入到記憶體中,將其放在執行時資料區的方法區內,然後在堆區建立一個java.lang.C
JVM 類載入機制:編譯器常量與初始化
1. 前言 最近在研究JVM虛擬機器類載入機制的時候,我們瞭解到了類載入機制的生命週期以及在準備階段,JVM虛擬機器會對類的靜態變數進行初始化,這個時候只是會將靜態變數初始化為預設的初始值。對靜態變數的定義的初始化值,將會被封裝到clinit()方法中,直到初始化階段進行初始化。但是對於
JVM 類載入機制深入淺出
從類被載入到虛擬機器記憶體中開始,到卸御出記憶體為止,它的整個生命週期分為7個階段,載入(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Usi
JVM系列第7講:JVM 類載入機制
當 Java 虛擬機器將 Java 原始碼編譯為位元組碼之後,虛擬機器便可以將位元組碼讀取進記憶體,從而進行解析、執行等整個過程,這個過程我們叫:Java 虛擬機器的類載入機制。JVM 虛擬機器執行 class 位元組碼的過程可以分為七個階段:載入、驗證、準備、解析、初始化、使用、解除安裝。 在開始聊之前,先