淺談JVM-圖解類加載機制


一、目錄

二、類加載機制流程

1、什么是類加載機制?

  JVM把class文件加載到內存里面,並對數據進行校驗、准備、解析和初始化,最終能夠被形成被JVM可以直接使用的Java類型的過程。

2、類加載流程圖

3、加載

  1. 將class文件加載在內存中。
  2. 將靜態數據結構(數據存在於class文件的結構)轉化成方法區中運行時的數據結構(數據存在於JVM時的數據結構)。
  3. 在堆中生成一個代表這個類的java.lang.Class對象,作為數據訪問的入口。

4、鏈接

 鏈接就是將Java類的二進制代碼合並到java的運行狀態中的過程。

  • 驗證:確保加載的類符合JVM規范與安全。
  • 准備:為static變量在方法區中分配空間,設置變量的初始值。例如static int a=3,在此階段會a被初始化為0,其他數據類型參考成員變量聲明。
  • 解析:虛擬機將常量池的符號引用轉變成直接引用。例如"aaa"為常量池的一個值,直接把"aaa"替換成存在於內存中的地址。
    • 符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。符號引用與虛擬機實現的內存布局無關,引用 的目標並不一定已經加載到內存中。
    • 直接引用:直接引用可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用是與虛擬機實現的內存布局相關的,如果有了直接引用,那么引用的目標必定已經在內存中存在。

5、初始化

  初始化階段是執行類構造器<clinit>()方法。在類構造器方法中,它將由編譯器自動收集類中的所有類變量的賦值動作(准備階段的a正是被賦值a)和靜態變量與靜態語句塊static{}合並,初始化時機后續再聊。

6、使用

  正常使用。

7、卸載

  GC把無用對象從內存中卸載。

三、類加載與初始化時機

1、類加載時機

  當應用程序啟動的時候,所有的類會被一次性加載嗎?估計你早已知道答案,當然不能,因為如果一次性加載,內存資源有限,可能會影響應用程序的正常運行。那類什么時候被加載呢?例如,A a=new A(),一個類真正被加載的時機是在創建對象的時候,才會去執行以上過程,加載類。當我們測試的時候,最先加載擁有main方法的主線程所在類。

2、類初始化時機

 主動引用(發生類初始化過程)

  1. new一個對象。
  2. 調用類的靜態成員(除了final常量)和靜態方法。
  3. 通過反射對類進行調用。
  4. 虛擬機啟動,main方法所在類被提前初始化。
  5. 初始化一個類,如果其父類沒有初始化,則先初始化父類。

 被動引用(不會發生類的初始化)

  1. 當訪問一個靜態變量時,只有真正聲明這個變量的類才會初始化。(子類調用父類的靜態變量,只有父類初始化,子類不初始化)。
  2. 通過數組定義類引用,不會觸發此類的初始化。
  3. final變量不會觸發此類的初始化,因為在編譯階段就存儲在常量池中。

四、圖解分析類加載

 1 public class ClassLoaderProduce {
 2     static int d=3;
 3     static{
 4         System.out.println("我是ClassLoaderProduce類");
 5     }
 6     public static void main(String [] args){
 7         int b=0;
 8         String c="hello";
 9         SimpleClass simpleClass=new SimpleClass();
10         simpleClass.run();
11     }
12 }
13 
14 class SimpleClass{
15     static int  a=3;
16     static{
17         a=100;
18         System.out.println(a);
19     }
20 
21     public SimpleClass(){
22         System.out.println("對類進行加載!");
23     }
24 
25     public void run(){
26         System.out.println("我要跑跑跑!");
27     }
28 }

步驟一:裝載ClassLoaderProduce類,在方法區生成動態數據結構(靜態變量、靜態方法、常量池、類代碼),並且在堆中生成java.lang.Class對象;然后進行鏈接

步驟二:初始化:把static{}與靜態變量合並存放在類構造器當中,對靜態變量賦值。 1-5行執行完畢。

步驟三:執行main方法,首先在棧里面生成一個main方法的棧禎,定義變量b、c,注意此處的變量b、c存儲的常量池存儲的變量的地址,如圖所示。

步驟四:創建SimpleClass對象;跟上面步驟類似:加載-鏈接-初始化。然后,調用run()方法的時候,它會通過classLoader局部變量的地址尋找到類的class對象並且調用run()方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM