jvm內存與類加載機制

JVM內存結構

    待補充.....

 

類加載機制

一:java類的加載過程

編譯後的Java類是以字節碼的形式存在的,它只有被加載到虛擬機內存中才能被使用,它是如何被加載到內存中的呢?

下圖爲類加載到內存的機制:

作者:夏昊
鏈接:https://www.zhihu.com/question/20097631/answer/817071740
來源:知乎
著作權歸作者所有,轉載請聯繫作者獲得授權。
 

1:加載

在加載(注意和類加載是不同的概念)階段虛擬機需要完成三件事

(1).通過一個類的全限定名(類名全稱,帶包路徑的用點隔開,例如: java.lang.String)來獲取其定義的二進制字節流(被編譯以後的字節碼文件就是二進制的)。

(2).將這個字節流所代表的靜態存儲結構(字節碼文件就是其中一種)轉化爲方法區的運行時數據結構(能夠在虛擬機中存儲的結構)。

(3).在Java堆中生成一個代表這個類的 java.lang.Class對象(用於表示這個類的信息),作爲對方法區中這些數據的訪問入口。

2:驗證:

驗證,準備,解析統稱爲連接,作爲連接階段的第一步,驗證的主要作用是保證加載進來的二進制流中的信息是符合當前虛擬機要求的,並且不會對虛擬機的安全造成危害。主要包括:

(1)文件格式驗證:主要是驗證二進制流是否符合class文件格式的規範,並且能被當前的虛擬機處理,例如:主次版本號是否在當前虛擬機處理範圍之內,常量池的常量中是否有不被支持的常量類型。只有通過了這個階段的驗證後字節流纔會進入內存的方法區中進行存儲,從這裏我們可以看出加載和驗證階段是交叉進行的,加載還未完成,文件格式驗證就已經開始了。

(2)元數據驗證:對字節碼描述的信息進行語義分析以保證其描述的信息符合java語言規範的要求:例如:這個類是否有父類(所有類除了Object都應該有父類)

(3)字節碼驗證:確定程序語義是合法的符合邏輯的,如將子類對象賦給父類類型是符合邏輯的,反之,將父類對象賦給子類類型則是不合法的。

(4)符號引用驗證:可以對常量池中各種符號引用的信息進行匹配性校驗。例如:符號引用中通過字符串描述的全限定名是否能找到對應的類。

3:準備

準備階段會爲靜態變量分配內存並設置初始值,注意:該初始值爲數據類型的零值例如:

Public static int num = 3;在準備階段會將num值設置爲0而不是3.只有在初始化階段纔會賦值爲3.

4:解析

解析階段是把類中的符號引用轉換爲直接引用的過程:

符號引用:在編譯的時候是不知道類所引用的類的具體地址,因此只能使用符號引用來代替,比如:com.Student類引用了com.Grade類,編譯時Student類並不知道Grade類在內存中的實際地址,只能用符號 com.Grade。

直接引用;引用的實際內存地址。

5:初始化

初始化階段是類加載過程的最後一步,在這個階段會根據程序中的賦值語句給變量賦值,當有繼承關係時先初始化父類,再初始化子類。如果該類還沒有被加載和連接,那麼初始化之前先加加載和連接。

什麼時候會進行初始化呢?

1. 使用new關鍵字實例化對象的時候

2. 讀取或設置一個類的靜態字段的時候

3. 調用一個類的靜態方法的時候

4. 對類進行反射調用的時候

5. 當虛擬機啓動時執行一個類的main方法,會先初始化這個類

二:類加載器:

加載階段中的第一步:“通過一個類的全限定名來獲取其定義的二進制字節流”是通過類加載器來完成的,類加載器分爲三種:

1. 啓動類加載器(BootStrap ClassLoader),這個類加載器負責將jdk\jre\lib下的類庫加載到內存中,啓動類加載器無法被應用程序直接使用。

2. 擴展類加載器(Extension ClassLoader),它負責加載jdk\jre\lib\ext中的類庫。開發者可以直接使用擴展類加載器。

3. 應用程序類加載器(Application ClassLoader),它用來加載classpath路徑(src路徑下的文件在編譯後會放到WEB-INF/classes路徑下。默認的classpath是在這裏)指定的類。開發者可以直接使用這個類加載器,如果如果應用程序中沒有定義自己的類加載器,一般情況下這個就是程序中默認的類加載器。

我們的應用程序都是由這三種類加載器互相配合進行加載的,如果有必要還可以加入自己定義的類加載器。

三:雙親委派模型

 

下圖爲雙親委派模型的類加載器的層次關係:


 

雙親委派模型的的工作過程是:如果一個類加載器收到了類加載的請求,它會把這個請求委派給父類加載器去完成,每一個層次的加載器都是如此,因此所有的加載請求最終都會傳送到頂層的啓動類加載器中。只有當父加載器反饋自己無法完成這個加載請求時(在自己的加載範圍內沒有搜索到該類),子加載器纔會嘗試自己去加載。例如:

1. 當應用程序類加載器加載一個類時,它會把類加載請求委派給擴展類加載器。

2. 擴展類加載器又把這個類加載請求委派給啓動類加載器。

3. 啓動類加載器如果加載失敗,在(jdk/jre/lib)裏沒有找到這個類,會使用擴展類加載器進行加載。

4. 擴展類加載器如果加載失敗,在(jdk/jre/lib/ext)裏沒有找到這個類,會使用應用程序類加載器來加載

5. 應用程序加載器加載失敗則會報:ClassNotFoundException異常。

使用雙親委派模型的意義:

例如類java.lang.Object存放在rt.jar中,無論哪個類加載器要加載這個類,最終都會委派啓動類加載器來加載,如果沒有雙親委派模型用戶自己編寫了一個java.lang.Object類,放到ClassPath中,系統中將會出現多個不同的Object類,應用程序也會變得混亂。

參考:https://www.zhihu.com/collection/86433369

 

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