java虛擬機具有語言無關系,它只和“class檔案“這種特定的二進制檔案格式系結,
不同語言的編譯器將對應的程式編譯成位元組碼檔案(*.class),送給jvm執行,
class檔案本質上就是一張表,由各類資料項構成,
- 2.1、魔數(是否可以被虛擬機執行)和class檔案版本
- 2.2、常量池(兩大類常量:字面量與符號參考)
- 2.3、訪問標志(識別訪問資訊)
- 2.4、類索引、父類索引和介面索引集合
- 2.5、欄位表集合
- 2.6、方法表集合
————————————————————————————————————————————————
一.類加載時機:
共5種情形為主動參考,有且僅有此5種會觸發初始化,其他方式全部為被動參考,不會觸發類的初始化
5種情形:
- 遇到new (實體化物件),getstatic(讀取一個類的靜態欄位) ,putstatic(設定一個類的靜態欄位), invokestatic(呼叫一個類的靜態方法)這4條指令,若類之前沒有初始化,需要先對其進行初始化,
- 使用 java.lang.reflect包的方法對類進行反射呼叫時,若類之前沒有初始化,需要先對其進行初始化,
- 當初始化一個類,其父類之前沒有初始化,需要先對其父類進行初始化,
- 當虛擬機啟動時,主類會先被初始化(包含main方法的類),
- 使用jdk7的動態語言支持時,如果一個決議結果與靜態欄位或靜態方法有關,所對應的類之前沒有初始化,需要先對其進行初始化,
二.類加載程序

其中類加載的程序包括了加載、驗證、準備、決議、初始化五個階段,
在這五個階段中,加載、驗證、準備和初始化這四個階段發生的順序是確定的,而決議階段則不一定,它在某些情況下可以在初始化階段之后開始,這是為了支持Java語言的運行時系結(動態系結),
另外注意這里的幾個階段是按順序開始,而不是按順序進行或完成,因為這些階段通常都是互相交叉地混合進行的,通常在一個階段執行的程序中呼叫或激活另一個階段,
1.加載
加載程序完成一下3件事:
- 獲取一個類全限定名的二進制位元組流,
- 將靜態存盤結構轉換為方法區的運行時資料結構
- 記憶體中生產class物件,作為方法區中該類的資料訪問入口
加載與連接階段交叉進行(但是開始時間順序固定),
2.驗證
四個階段:檔案格式驗證(驗證規范),元資料驗證(語意校驗),位元組碼驗證(資料流與控制流分析),符號參考認證(符號參考的匹配校驗),
3.準備:正式分配記憶體并設定變數初始值,記憶體在方法區內分配,
4.決議:將常量池內的符號參考替換為直接參考
符號參考:一組可以無歧義定位到目標的符號
直接參考:直接指向目標的指標,相對偏移量或者能間接定位到目標的句柄
5.初始化:根據程式制定的主管計劃去初始化變數與資源,
三.類加載器
1.類與類加載器,每一個類加載器都有其獨立的類名稱空間,兩個類由同一個類加載器加載才可能相同,否則必然不同(equals,isAssignableFrom,isInstance)
2.雙親委派機制,
類加載器負責加載所有的類,其為所有被載入記憶體中的類生成一個java.lang.Class實體物件,一旦一個類被加載如JVM中,同一個類就不會被再次載入了,正如一個物件有一個唯一的標識一樣,一個載入JVM的類也有一個唯一的標識,在Java中,一個類用其全限定類名(包括包名和類名)作為標識;但在JVM中,一個類用其全限定類名和其類加載器作為其唯一標識,例如,如果在pg的包中有一個名為Person的類,被類加載器ClassLoader的實體kl負責加載,則該Person類對應的Class物件在JVM中表示為(Person.pg.kl),這意味著兩個類加載器加載的同名類:(Person.pg.kl)和(Person.pg.kl2)是不同的、它們所加載的類也是完全不同、互不兼容的,
JVM預定義有三種類加載器,當一個 JVM啟動的時候,Java開始使用如下三種類加載器:

1)根類加載器(bootstrap class loader):它用來加載 Java 的核心類,是用原生代碼來實作的,并不繼承自 java.lang.ClassLoader(負責加載$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++實作,不是ClassLoader子類),由于引導類加載器涉及到虛擬機本地實作細節,開發者無法直接獲取到啟動類加載器的參考,所以不允許直接通過參考進行操作,
2)擴展類加載器(extensions class loader):它負責加載JRE的擴展目錄,lib/ext或者由java.ext.dirs系統屬性指定的目錄中的JAR包的類,由Java語言實作,父類加載器為null,
3)系統類加載器(system class loader):被稱為系統(也稱為應用)類加載器,它負責在JVM啟動時加載來自Java命令的-classpath選項、java.class.path系統屬性,或者CLASSPATH換將變數所指定的JAR包和類路徑,程式可以通過ClassLoader的靜態方法getSystemClassLoader()來獲取系統類加載器,如果沒有特別指定,則用戶自定義的類加載器都以此類加載器作為父加載器,由Java語言實作,父類加載器為ExtClassLoader,
類加載器加載Class大致要經過如下8個步驟:
- 檢測此Class是否載入過,即在緩沖區中是否有此Class,如果有直接進入第8步,否則進入第2步,
- 如果沒有父類加載器,則要么Parent是根類加載器,要么本身就是根類加載器,則跳到第4步,如果父類加載器存在,則進入第3步,
- 請求使用父類加載器去載入目標類,如果載入成功則跳至第8步,否則接著執行第5步,
- 請求使用根類加載器去載入目標類,如果載入成功則跳至第8步,否則跳至第7步,
- 當前類加載器嘗試尋找Class檔案,如果找到則執行第6步,如果找不到則執行第7步,
- 從檔案中載入Class,成功后跳至第8步,
- 拋出ClassNotFountException例外,
- 回傳對應的java.lang.Class物件,
四、類加載機制:
1.JVM的類加載機制主要有如下3種,
- 全盤負責:所謂全盤負責,就是當一個類加載器負責加載某個Class時,該Class所依賴和參考其他Class也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入,
- 雙親委派:所謂的雙親委派,則是先讓父類加載器試圖加載該Class,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類,通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委托給父加載器,依次遞回,如果父加載器可以完成類加載任務,就成功回傳;只有父加載器無法完成此加載任務時,才自己去加載,
- 快取機制,快取機制將會保證所有加載過的Class都會被快取,當程式中需要使用某個Class時,類加載器先從快取區中搜尋該Class,只有當快取區中不存在該Class物件時,系統才會讀取該類對應的二進制資料,并將其轉換成Class物件,存入緩沖區中,這就是為很么修改了Class后,必須重新啟動JVM,程式所做的修改才會生效的原因,
2雙親委派機制:
雙親委派機制,其作業原理的是,如果一個類加載器收到了類加載請求,它并不會自己先去加載,而是把這個請求委托給父類的加載器去執行,如果父類加載器還存在其父類加載器,則進一步向上委托,依次遞回,請求最終將到達頂層的啟動類加載器,如果父類加載器可以完成類加載任務,就成功回傳,倘若父類加載器無法完成此加載任務,子加載器才會嘗試自己去加載,這就是雙親委派模式,即每個兒子都很懶,每次有活就丟給父親去干,直到父親說這件事我也干不了時,兒子自己才想辦法去完成,
雙親委派機制的優勢:采用雙親委派模式的是好處是
- Java類隨著它的類加載器一起具備了一種帶有優先級的層次關系,通過這種層級關可以避免類的重復加載,當父親已經加載了該類時,就沒有必要ClassLoader再加載一次,
- 其次是考慮到安全因素,java核心api中定義型別不會被隨意替換,假設通過網路傳遞一個名為java.lang.Integer的類,通過雙親委托模式傳遞到啟動類加載器,而啟動類加載器在核心Java API發現這個名字的類,發現該類已被加載,并不會重新加載網路傳遞的過來的java.lang.Integer,而直接回傳已加載過的Integer.class,這樣便可以防止核心API庫被隨意篡改,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/86533.html
標籤:.NET Core
上一篇:如何運用領域驅動設計 - 物體
