面試連環call
- Java文件是如何被加載到內存中的?
- Java類的聲明周期都有哪些階段?
- JVM加載的class文件都有哪些來源?
- JVM在加載class文件時,何時判斷class文件的格式是否符合要求?
類生命周期
一個類從被加載到虛擬機內存開始,到卸載出內存為止,它的整個生命周期將會經歷加載、驗證、準備、解析、初始化、使用和卸載七個階段,其中驗證、準備、解析三個部分統稱為連接。
- 加載、驗證、準備、初始化和卸載這五個階段的順序是確定的,類型的加載過程必須按照這種順序按步就班地開始,
- 解析階段順序則不固定,它可以在初始化之前,而在某些情況下也可以在初始化階段之后,這是為了支持Java語言的運行時綁定特性。
類加載過程
系統加載 Class 類型的文件主要三步:加載->連接->初始化。連接過程又可分為三步:驗證->準備->解析。
加載
加載是類加載過程的第一個階段,在加載階段,虛擬機需要完成以下三件事情
- 通過一個類的全限定名來獲取其定義的二進制字節流。
- 將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
- 在Java堆中生成一個代表這個類的java.lang.Class對象,作為對方法區中這些數據的訪問入口。
加載.class文件的方式
- 從本地文件系統中直接加載
- 通過網絡下載.class文件
- 從zip,jar等歸檔文件中加載.class文件
- 從專有數據庫中提取.class文件
- 將Java源文件動態編譯為.class文件
鏈接
驗證
確保被加載的類的正確性
驗證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全。
- 文件格式驗證(Class 文件格式檢查)
- 元數據驗證(字節碼語義檢查)
- 字節碼驗證(程序語義檢查)
- 符號引用驗證(類的正確性檢查)
準備
為類的靜態變量分配內存,并將其初始化為默認值
- 這時候進行內存分配的僅包括類變量(
static
),而不包括實例變量,實例變量會在對象實例化時隨著對象一塊分配在Java堆中。 - 這里所設置的初始值通常情況下是數據類型默認的零值(如
0
、0L
、null
、false
等),而不是被在Java代碼中被顯式地賦予的值。
注意:JDK 7 之前,HotSpot 使用永久代來實現方法區的時候,類變量所使用的內存都應當在 永久的 中進行分配。 而在 JDK 7 及之后,HotSpot 已經把原本放在永久代的字符串常量池、靜態變量等移動到堆中,這個時候類變量則會隨著 Class 對象一起存放在 Java 堆中。
解析
虛擬機將常量池內的符號引用替換為直接引用的過程
解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用限定符 7 類符號引用進行
- 符號引用
- 符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能夠無歧義的定位到目標即可
- 直接引用
- 直接指向目標的指針(比如,指向Class類型、類變量、類方法的直接引用可能是指向方法區的指針)
- 相對偏移量(比如,指向實例變量、實例方法的直接引用都是偏移量)
- 一個能間接定位到目標的句柄
類的初始化
當一個JVM在啟動之后,其中可能包含的類非常多,是不是每個類都會被初始化呢?答案是否定的。
JVM對類的初始化是一個延遲機制,當一個類在首次使用的時候才會被初始化,在同一個運行時package下,一個Class只會被初始化一次。
《Java虛擬機規范》則是嚴格規定了有且只有6種情況下必須立即對類進行初始化(而加載、驗證、準備自然需要在此之前開始)
-
通過new關鍵字會導致類的初始化
-
訪問類的靜態變量,包括讀取和更新會導致類的初始化
-
訪問類的靜態方法,也會導致類初始化
-
初始化子類會導致父類被初始化
-
對某個類進行反射操作。會導致類被初始化
-
啟動類,就是執行main函數所在的類會導致該類被初始化
注意:構造某個類的數組時并不會導致該類的初始化
參考內容
-
《深入理解 Java 虛擬機》
-
十分鐘搞懂Java類加載
-
Java 類加載機制
-
類加載過程詳解
-
JVM:類加載過程
更多驚喜
我還將定期分享:
-
最新互聯網資訊:讓你時刻掌握行業動態。
-
AI前沿新聞:緊跟技術潮流,不斷提升自我。
-
技術分享與職業發展:助你在職業生涯中走得更遠、更穩。
-
程序員生活趣事:讓你在忙碌的工作之余找到共鳴與樂趣。
關注回復【1024】驚喜等你來拿!