1. 程式人生 > >Java 類的載入機制

Java 類的載入機制

簡介

Java虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的載入機制。

類的生命週期

類從被載入到虛擬機器記憶體中開始,到卸載出記憶體為止,它的整個生命週期包括了:載入(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和解除安裝(Unloading)七個階段。

其中驗證、準備和解析三個部分統稱為連線(Linking)。

載入、驗證、準備、初始化和解除安裝這五個階段的順序是確定的,類的載入過程必須按照這個順序來按部就班地開始,而解析階段則不一定,它在某些情況下可以在初始化階段後再開始。類的生命週期的每一個階段通常都是互相交叉混合式進行的,通常會在一個階段執行的過程中呼叫或啟用另外一個階段。

類載入的時機

主動引用

一個類被主動引用之後會觸發初始化過程(載入,驗證,準備需再此之前開始)

  • 1)遇到new、get static、put static或invoke static這4條位元組碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。生成這4條指令最常見的Java程式碼場景是:使用new關鍵字例項化物件時、讀取或者設定一個類的靜態欄位(被final修飾、已在編譯器把結果放入常量池的靜態欄位除外)時、以及呼叫一個類的靜態方法的時候。

  • 2)使用java.lang.reflect包的方法對類進行反射呼叫的時候,如果類沒有進行過初始化,則需要先觸發其初始化。

  • 3)當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要觸發父類的初始化。

  • 4)當虛擬機器啟動時,使用者需要指定一個執行的主類(包含main()方法的類),虛擬機器會先初始化這個類。

  • 5)當使用jdk7+的動態語言支援時,如果java.lang.invoke.MethodHandle例項最後的解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法控制代碼,並且這個方法控制代碼所對應的類沒有進行過初始化,則需要先觸發器 初始化。

被動引用

一個類如果是被動引用的話,該類不會觸發初始化過程

  • 1)通過子類引用父類的靜態欄位,不會導致子類初始化。對於靜態欄位,只有直接定義該欄位的類才會被初始化,因此當我們通過子類來引用父類中定義的靜態欄位時,只會觸發父類的初始化,而不會觸發子類的初始化。

  • 2)通過陣列定義來引用類,不會觸發此類的初始化。

  • 3)常量在編譯階段會存入呼叫類的常量池中,本質上沒有直接引用到定義常量的類,因此不會觸發定義常量的類的初始化。

類載入過程

1、載入

在載入階段,虛擬機器需要完成以下三件事情:

  • 1)通過一個類的全限定名稱來獲取定義此類的二進位制位元組流。

  • 2)將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構。

  • 3)在java堆中生成一個代表這個類的java.lang.Class物件,作為方法區這些資料的訪問入口。
    相對於類載入過程的其他階段,載入階段是開發期相對來說可控性比較強,該階段既可以使用系統提供的類載入器完成,也可以由使用者自定義的類載入器來完成,開發人員可以通過定義自己的類載入器去控制位元組流的獲取方式。

2、驗證

驗證的目的是為了確保Class檔案中的位元組流包含的資訊符合當前虛擬機器的要求,而且不會危害虛擬機器自身的安全。不同的虛擬機器對類驗證的實現可能會有所不同,但大致都會完成以下四個階段的驗證:檔案格式的驗證、元資料的驗證、位元組碼驗證和符號引用驗證。

  • 1)檔案格式的驗證:驗證位元組流是否符合Class檔案格式的規範,並且能被當前版本的虛擬機器處理,該驗證的主要目的是保證輸入的位元組流能正確地解析並存儲
    於方法區之內。經過該階段的驗證後,位元組流才會進入記憶體的方法區中進行儲存,後面的三個驗證都是基於方法區的儲存結構進行的。

  • 2)元資料驗證:對類的元資料資訊進行語義校驗(其實就是對類中的各資料型別進行語法校驗),保證不存在不符合Java語法規範的元資料資訊。

  • 3)位元組碼驗證:該階段驗證的主要工作是進行資料流和控制流分析,對類的方法體進行校驗分析,以保證被校驗的類的方法在執行時不會做出危害虛擬機器安全的行為。

  • 4)符號引用驗證:這是最後一個階段的驗證,它發生在虛擬機器將符號引用轉化為直接引用的時候(解析階段中發生該轉化,後面會有講解),主要是對類自身以外的資訊(常量池中的各種符號引用)進行匹配性的校驗。

3、準備

準備階段是正式為類變數分配記憶體並設定類變數初始值的階段,這些記憶體都將在方法區中進行分配。

  • 1)這時候進行記憶體分配的僅包括類變數(static),而不包括例項變數,例項變數會在物件例項化時隨著物件一塊分配在Java堆中。

  • 2)這裡所設定的初始值通常情況下是資料型別預設的零值(如0、0L、、false等),而不是被在Java程式碼中被顯式地賦予的值。

4、解析

解析階段是虛擬機器將常量池內的符號引用替換為直接引用的過程。

符號引用(Symbolic Reference):

符號引用以一組符號來描述所引用的目標,符號引用可以是任何形式的字面量,符號引用與虛擬機器實現的記憶體佈局無關,引用的目標並不一定已經在記憶體中。

直接引用(Direct Reference):

直接引用可以是直接指向目標的指標、相對偏移量或是一個能間接定位到目標的控制代碼。直接引用是與虛擬機器實現的記憶體佈局相關的,同一個符號引用在不同的虛擬機器例項上翻譯出來的直接引用一般都不相同,如果有了直接引用,那引用的目標必定已經在記憶體中存在。

  • 1)類或介面的解析:判斷所要轉化成的直接引用是對陣列型別,還是普通的物件型別的引用,從而進行不同的解析。

  • 2)欄位解析:對欄位進行解析時,會先在本類中查詢是否包含有簡單名稱和欄位描述符都與目標相匹配的欄位,如果有,則查詢結束;如果沒有,則會按照繼承關係從上往下遞迴搜尋該類所實現的各個介面和它們的父介面,還沒有,則按照繼承關係從上往下遞迴搜尋其父類,直至查詢結束。

  • 3)類方法解析:對類方法的解析與對欄位解析的搜尋步驟差不多,只是多了判斷該方法所處的是類還是介面的步驟,而且對類方法的匹配搜尋,是先搜尋父類,再搜尋介面。

  • 4)介面方法解析:與類方法解析步驟類似,只是介面不會有父類,因此,只遞歸向上搜索父介面就行了。

5、初始化

類初始化階段是類載入過程的最後一步,前面的類載入過程中,除了載入(Loading)階段使用者應用程式可以通過自定義類載入器參與之外,其餘動作完全由虛擬機器主導和控制。到了初始化階段,才真正開始執行類中定義的Java程式程式碼。

初始化階段是執行類構造器<clinit>方法的過程。

  • 1)<clinit>方法是由編譯器自動收集類中的所有類變數的賦值動作和靜態語句塊(static{}塊)中的語句合併產生的,編譯器收集的順序由語句在原始檔中出現的順序所決定。

  • 2)<clinit>方法與類的建構函式不同,它不需要顯式地呼叫父類構造器,虛擬機器會保證在子類的<clinit>方法執行之前,父類的<clinit>方法已經執行完畢,因此在虛擬機器中第一個執行的<clinit>方法的類一定是java.lang.Object。

  • 3)由於父類的<clinit>方法先執行,也就意味著父類中定義的靜態語句塊要優先於子類的變數賦值操作。

  • 4)<clinit>方法對於類或者介面來說並不是必需的,如果一個類中沒有靜態語句塊也沒有對變數的賦值操作,那麼編譯器可以不為這個類生成<clinit>方法。

  • 5)介面中可能會有變數賦值操作,因此介面也會生成<clinit>方法。但是介面與類不同,執行介面的<clinit>方法不需要先執行父介面的<clinit>方法。只有當父介面中定義的變數被使用時,父接口才會被初始化。另外,介面的實現類在初始化時也不會執行介面的<clinit>方法。

  • 6)虛擬機器會保證一個類的<clinit>方法在多執行緒環境中被正確地加鎖和同步。如果有多個執行緒去同時初始化一個類,那麼只會有一個執行緒去執行這個類的<clinit>方法,其它執行緒都需要阻塞等待,直到活動執行緒執行<clinit>方法完畢。如果在一個類的<clinit>方法中有耗時很長的操作,那麼就可能造成多個程序阻塞。

相關推薦

從阿里巴巴面試題到java載入機制

首先很經典的阿里巴巴面試題 加上我自己的一些疑惑程式碼 public class Text { public static int k = 0; public final int k1 = 3; //自己加的 public static Text t1 = new Text("

java載入機制和自定義載入

類載入順序 上圖所示的是類載入的順序,按照大的順序可以分為載入、連結、初始化 其中連結又可以分成驗證、準備、解析三個步驟 載入 1.將類的class檔案讀入到記憶體中 載入類檔案的方式有: 1. 本機檔案載入 2.jar包載入 3.網路載入 4.原始檔動態編譯載入

淺談Java載入機制

最近在學習 Tomcat 架構,其中很重要的一個模組是類載入器,因為以前學習的不夠深入,所以趁這個機會好好把類載入機制搞明白。 概述 類載入器主要分為兩類,一類是 JDK 預設提供的,一類是使用者自定義的。 JDK 預設提供三種類載入器 Bootstrap ClassLo

深入理解Java載入機制(一)

1 前言: 在上一篇文章一文讓你明白 Java 位元組碼中, 我們瞭解了java位元組碼的解析過程,那麼在接下來的內容中,我們來了解一下類的載入機制。 2 題外話 Java的核心是什麼?當然是JVM了,所以說了解並熟悉JVM對於我們理解Java語言非常重要,不管你是做Java還是Andr

Java 載入機制詳解

一、類載入器   類載入器(ClassLoader),顧名思義,即載入類的東西。在我們使用一個類之前,JVM需要先將該類的位元組碼檔案(.class檔案)從磁碟、網路或其他來源載入到記憶體中,並對位元組碼進行解析生成對應的Class物件,這就是類載入器的功能。我們可以利用類載入器,實現類的動態載入。 二、類的

Java載入機制

類載入機制 概念 類載入器把class檔案中的二進位制資料讀入到記憶體中,存放在方法區,然後在堆區建立一個java.lang.Class物件,用來封裝類在方法區內的資料結構。 1、載入: 查詢並載入類的二進位制資料(把class檔案裡面的資訊載入到記憶體裡面) 2

JVM——Java載入機制

class Singleton{ private static Singleton singleton = new Singleton(); public static int value1; public static int valu

JAVA 載入機制學習筆記

JAVA 類生命週期     如上圖所示,Java類的生命週期如圖所示,分別為載入、驗證、準備、解析、初始化、使用、解除安裝。其中驗證、準備、解析這三個步驟統稱為連結。   載入:JVM根據全限定名來獲取一段二進位制位元組流,將二進位制流轉化為方法區的執行時資料結構,在記憶體中生成一個代表

Java面試題】之類載入:從面試題分析Java載入機制

 “載入”(Loading)階段是“類載入”(Class Loading)過程的第一個階段,在此階段,虛擬機器需要完成以下三件事情:        1、 通過一個類的全限定名來獲取定義此類的二進位制位元組流。        2、 將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結

jvm之java載入機制載入器(ClassLoader)的詳解

     當程式主動使用某個類時,如果該類還未被載入到記憶體中,則JVM會通過載入、連線、初始化3個步驟來對該類進行初始化。如果沒有意外,JVM將會連續完成3個步驟,所以有時也把這個3個步驟統稱為類載入或類初始化。                             

Java-載入機制

Java-類載入機制 摘要 本文簡要介紹Java載入機制,還會介紹雙親委派機制的破壞,執行緒上下文載入器,以及JDBC Driver是如何自動載入的。 未完成 0x01 Java類載入機制 1.1 簡介 當前版本jdk是採用雙親委派機制: 子ClassL

談談 Java 載入機制

最近在學習 Tomcat 架構,其中很重要的一個模組是類載入器,因為以前學習的不夠深入,所以趁這個機會好好把類載入機制搞明白。 概述 類載入器主要分為兩類,一類是 JDK 預設提供的,一類是使用者自定義的。 JDK 預設提供三種類載入器: Bootstrap

java 載入機制--載入時機

相對於看很多父類和子類如何如何初始化的文章,瞭解java虛擬機器的類載入機制之後都會覺得,噢,原來是這個樣子!這裡僅就類的載入時機做個學習筆記,更多內容改日再寫。詳情請看《深入理解java虛擬機器》。 不同於例如c,cpp那些編譯時期就需要進行連線工作的語言,

Java 載入機制 ClassLoader Class.forName 記憶體管理 垃圾回收GC

類載入是Java程式執行的第一步,研究類的載入有助於瞭解JVM執行過程,並指導開發者採取更有效的措施配合程式執行。 研究類載入機制的第二個目的是讓程式能動態的控制類載入,比如熱部署等,提高程式的靈活性和適應性。 一、簡單過程 Java

Java載入機制——案例分析

原文連結:http://blog.csdn.net/u013256816/article/details/50837863 在《Java虛擬機器類載入機制》一文中詳細闡述了類載入的過程,並舉了幾個例子進行了簡要分析,在文章的最後留了一個懸念給各位,這裡來揭開這個懸念。建

深入java static關鍵字 淺析java載入機制(解答java靜態方法或變數無法訪問非靜態資料)

想要清晰理解java語法,不瞭解java和jvm的機制是不行的,以前不理解java中用static修飾方法和變數為什麼不可以訪問非靜態方法和資料,現在明瞭,如果你也有相同的困惑,這篇部落格足以解惑,原創不易,轉載請宣告出處。 本文分為3大部分 static

java 載入機制

1.java虛擬機器中可以有多個類載入器。 2.系統預設有三個類載入器,每個類載入器負責特定的位置的類載入任務,分別是: BootStrap-->JRE/lib/rt.jar ExtClassLoader-->JRE/lib/ext/*.jar AppClass

阿里面試題,深入理解Java載入機制

類的生命週期 包括以下 7 個階段: 載入(Loading) 驗證(Verification) 準備(Pr

技術複習-java載入機制

java類載入過程 裝載 把class位元組碼載入到記憶體中。 連結 驗證、準備、解析 初始化 類的靜態變數、靜態程式碼庫

JVM 第三篇:Java 載入機制

![](https://cdn.geekdigging.com/java/java_header.jpg) > 本文內容過於硬核,建議有 Java 相關經驗人士閱讀。 ## 1. 什麼是類的載入? 類的載入指的是將類的 .class 檔案中的二進位制資料讀入到記憶體中,將其放在執行時資料區的方法區內,然