類載入機制

HandKnock發表於2020-10-26

總結

類載入機制

把class檔案載入到記憶體,並對資料進行校驗,準備,解析,初始化,形成可以被虛擬機器直接使用的位元組碼

類載入的時機(觸發類的初始化)

  • 使用new關鍵字例項化物件
  • 讀取一個類的靜態程式碼塊
  • 使用java.lang.reflect包的方式對類進行反射呼叫

類載入過程

整個生命週期包括:載入、校驗、準備、解析、初始化、使用和解除安裝7個階段。

  1. 載入:通過一個類的全限定名來獲取定義此類的二進位制位元組流,將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構,在記憶體中生成一個代表這個類的Class物件,作為方法區這個類的各種資料的訪問入口
  2. 校驗:校驗是連線階段的第一步,這一階段的目的是確保Class檔案的位元組流中包含的資訊符合當前虛擬機器的要求,並且不會危害虛擬自身的安全。
  3. 準備:準備階段是正式為類變數分配記憶體並設定類變數初始值的階段,這些變數所使用的記憶體都將在方法去中進行分配。這時候進行記憶體分配的僅包括類變數(static),而不包括例項變數,例項變數將會在物件例項化時隨著物件一起分配在Java堆中。
  4. 解析:解析階段是虛擬機器將常量池內的符號(Class檔案內的符號)引用替換為直接引用(指標)的過程。
  5. 初始化:初始化階段是類載入過程的最後一步,開始執行類中定義的Java程式程式碼(位元組碼)。

載入

  • 通過全類名獲取class檔案的二進位制位元組流
  • 將位元組流所代表的靜態儲存結構轉換為方法區的執行時資料結構
  • 生成一個代表該類的class物件,作為方法區這些資料的訪問入口

校驗

  • 驗證Class檔案中二進位制位元組流符合虛擬機器的要求,不會涉及到虛擬機器的安全
  • 檔案格式驗證
  • 後設資料驗證
  • 位元組碼驗證

準備

  • 為類變數分配記憶體空間和設定初始值的階段
  • 為新生物件分配記憶體
為新生物件分配記憶體
  • 如果Java堆記憶體是規整連續的,採用“指標碰撞”的分配方式
  • 如果不是連續規整的,採用“空閒列表”分配方式

記憶體是否規整取決於垃圾收集器是否帶有壓縮整理功能

這個初始值和初始化階段的賦值不同,這裡指的是變數的預設初始值,如果是final修飾的變數,就是賦予程式碼裡制定的初始值

解析

虛擬機器將符號引用替換為直接引用的過程

https://blog.csdn.net/qq_34402394/article/details/72793119

初始化

根據程式程式碼去初始化變數和其他資源,建構函式

類載入器

BootstrapLoader/負責載入系統類

注意一個很重要的問題,就是java在邏輯上並不存在BootstrapKloader的實體,因為它是c++編寫的,所以列印其內容會是null

啟動類載入器主要載入 jre/lib下的jar檔案。

ExtClassLoader/負責載入擴充套件類//繼承類和實現類

擴充套件類載入器主要載入 jre/lib/ext 下的jar檔案。

AppClassLoader/負責載入應用類

應用程式類載入器主要載入 classpath 下的檔案

Android類載入器

對於Android而言,最終的apk檔案包含的是dex型別的檔案,dex檔案是將class檔案重新打包,打包的規則又不是簡單地壓縮,而是完全對class檔案內部的各種函式表,變數表進行優化,產生一個新的檔案,即dex檔案。因此載入這種特殊的Class檔案就需要特殊的類載入器DexClassLoader。

雙親委派模型

當載入一個類時,會優先使用父類載入器載入,當父類載入器無法載入時才會使用子類載入器去載入。這麼做的目的是為了避免類的重複載入

直譯器

對位元組碼逐條解釋執行,這種方式的執行速度相對會比較慢;重複執行需要重複解釋

JIT(即時編譯器)

在執行時,虛擬機器將會把這些頻繁呼叫的程式碼編譯成與本地平臺相關的機器碼,並進行優化,可重複執行,快取效率高

Class檔案位元組碼結構

魔數—副版本號—主版本號—常量池計數器—常量池資料區—訪問標誌—類索引—父類索引—介面計數器—介面資訊資料區—欄位計數器—欄位資訊資料區—方法計數器—方法資訊資料區—屬性計數器—屬性資訊資料區

魔數:class檔案的標誌,確定這個檔案是否為一個能被虛擬機器接收的class檔案

類B繼承A,A、B兩個類中都有靜態變數、成員變數、靜態程式碼塊、構造方法執行順序是什麼?

1.父類【靜態成員】和【靜態程式碼塊】,按在程式碼中出現的順序依次執行。
2.子類【靜態成員】和【靜態程式碼塊】,按在程式碼中出現的順序依次執行。
3.父類的【普通成員變數被普通成員方法賦值】和【普通程式碼塊】,按在程式碼中出現的順序依次執行。
4.執行父類的構造方法。
5.子類的【普通成員變數被普通成員方法賦值】和【普通程式碼塊】,按在程式碼中出現的順序依次執行。
6.執行子類的構造方法。

JVM在搜尋類的時候,又是如何判定兩個class是相同的呢?

JVM在判定兩個class是否相同時,不僅要判斷兩個類名是否相同,而且要判斷是否由同一個類載入器例項載入的。只有兩者同時滿足的情況下,JVM才認為這兩個class是相同的。

就算兩個class是同一份class位元組碼,如果被兩個不同的ClassLoader例項所載入,JVM也會認為它們是兩個不同class。

類的載入過程,Person person = new Person();為例進行說明。

1).因為new用到了Person.class,所以會先找到Person.class檔案,並載入到記憶體中;
2).執行該類中的static程式碼塊,如果有的話,給Person.class類進行初始化;
3).在堆記憶體中開闢空間分配記憶體地址;
4).在堆記憶體中建立物件的特有屬性,並進行預設初始化;
5).對屬性進行顯示初始化;
6).對物件進行構造程式碼塊初始化;
7).對物件進行與之對應的建構函式進行初始化;
8).將記憶體地址付給棧記憶體中的p變數

相關文章