JVM是面試必面的一個知識點,也是高階程式設計師必備的一個技能。以下是JVM整體核心內容,包括類載入系統,執行時資料區內部結構,執行引擎,本地方法介面。
首先來學習類的載入器,虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的載入機制。
類載入器子系統如下圖所示:
類載入器子系統負責從檔案系統或者網路中載入Class檔案,class檔案再檔案開頭有特定的檔案標識。
ClassLoader只負責class檔案的載入,至於是否可以執行,則由ExecutionEngine決定。
記載的類資訊存放於一塊稱為方法區的記憶體空間,除了類的資訊外,方法區還會存放執行時常量池資訊,可能還包括字串字面量和數字常量。
一、類載入過程
類載入過程分為載入,連結(驗證、準備、解析),初始化三個過程:
載入:
通過一個類的全限定名獲取定義此類的二進位制位元組流。
將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構。
在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的各種資料的訪問入口。
驗證:
目的是為了確保Class檔案的位元組流中包含資訊符合當前虛擬機器的要求,保證載入類的正確性,不會危害虛擬機器自身安全。
主要包括四種驗證:檔案格式驗證,元資料驗證,位元組碼驗證,符符號引用驗證。
準備:
為類變數分配記憶體並設定類變數初始值,即零值。
這裡不包含用final修飾的static,因為final在編譯的時候就會分配了,準備階段會顯示的初始化。
這裡不會為例項變數分配初始化,類變數會分配在方法區中,而例項變數會隨著物件一起分配到Java堆中。
解析:
解析階段是虛擬機器將常量池內的符號引用替換為直接引用的過程。
事實上,解析操作往往伴隨著JVM在執行完初始化之後再執行。
初始化:
初始化階段就是執行類構造器方法<Clint>()的過程。
此方法不需定義,是javac編譯器自動收集類中的所有類變數的賦值動作和靜態程式碼塊中的語句合併而來。
構造器方法中指令按語句在原始檔中出現的順序執行。
<clint>不同於類的構造器
若該類具有父類,JVM會保證子類的<clint> 執行前,父類的已經執行完畢。
虛擬機器必須保證一個類的<clint>方法在多執行緒中被同步加鎖。
二、類載入器
JVM支援兩種型別的類載入器,分別為引導類載入器(Bootstrap ClassLoader)和自定義類載入器(User-Defined ClassLoader)
從概念上來講,自定義類載入器一般指的是程式中由開發人員自定義的一類類載入器,但是Java虛擬機器規範沒有這麼定義,而是將所有派生於抽象類ClassLoader的類載入器都劃分為自定義類載入器
引導類載入器是C++語言寫的,而自定義類載入器是Java語言寫的。
絕大部分Java程式都會使用到以下3種系統提供的類載入器。
啟動類載入器(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()方法可以獲取到該類載入器。
三、雙親委派機制
Java虛擬機器對class檔案採用的是按需載入的方式,也就是說當需要使用該類時才會將它的class檔案載入到記憶體生成class物件。而且載入某個類的class檔案時,java虛擬機器採用的是雙親委派模式,即把請求交由給父類處理,它是一種任務委派模式。
工作原理:
1.如果一個類載入器收到類載入請求,它並不會自己先去載入,而是把這個請求委託給父類的記載器去執行;
2.如果父類載入器還存在其父類載入器,則進一步向上委託,依次遞迴,請求最終將到達頂層的啟動類載入器。
3.如果父類載入器可以完成類載入任務,就成功返回,倘若父類載入器無法完成此載入任務,子載入器才會嘗試自己去載入,這就是雙親委派模式。
優勢:
避免類重複載入。
保護程式安全,防止核心API被隨意篡改。