[Java Jvm]Java 類的加載重點記錄

一.Java類加載概述

類的加載過程是指,虛擬機把描述類的數據從Class文件中加載到內存,並對數據進行校驗,轉換解析,和初始化的過程,最終形成可以被虛擬機直接使用的java類型。

Java中,類的加載,連接和初始化過程都是在程序運行期間完成的,這種策略會令類加載時耗時增加性能開銷,但提供了更高的靈活性,可以動態擴展,可以在運行時再指定下載的類文件達到動態擴展程序的作用
在這裏插入圖片描述

二.Java類的生命週期:

在這裏插入圖片描述

1.加載

a. 通過一個全限定名來獲取定義此類的二進制字節流(可以從class文件中讀取,也可以從zip,jar,網絡等中讀取)

b.將字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構

c.在內存(可以是堆可以是方法區,未強制規定,看虛擬機實現)中生成一個代表這類的類對象,作爲方法區這個類的各種數據的訪問入口.


Link,鏈接階段

2.驗證:

爲了確保Class文件的字節流信息符合要求,且不會有危害(Class文件並非只可以通過java編譯而來,還可以是其他生成方式,可以產生惡意語義)

大致過程:文件格式驗證,元數據驗證,字節碼驗證,符號引用驗證.

重要但非必要,多次驗證過的代碼可以在實施階段通過參數將驗證關閉,以縮短虛擬機類加載時間.

3.準備:

正式爲類變量(被static修飾的變量)分配內存並設置類變量初始值的階段.(實例變量在類初始化時設置到堆上,常量會在準備階段設置爲指定值)

4.解析:

解析階段不固定,可以在初始化錢也可以在初始化後進行,是虛擬機將常量池內的符號引用替換爲直接引用的過程

將常量池的符號引用替換爲直接引用的過程


5.初始化:

初始化階段是執行類構造器()方法的過程.
<clinit>()方法石油編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊中的語句合併產生的,編譯器手機的順序由語句在源文件中出現的順序所決定,

靜態語句塊中只能訪問到定義在靜態語句塊之前的變量,定義在它之後的變量,在前面的靜態語句塊可以賦值但不能訪問.

<clinit>()無需顯示調用父類構造其,虛擬機會保證在子類的()執行之前父類的()方法已經執行完畢.因此虛擬機中第一被執行的()方法的類肯定是Object類.

如果一類中無靜態語句塊,也沒有對變量賦值的操作,編譯器可以不爲其生成()方法.

初始化階段,有且只有5種情況必須立即對類進行初始化:

1.遇到new,getstatic,putstatic,invokestatic這4個字節碼指令時,如果類沒有進行初始化則需要先觸發其初始化;
該4條指令的場景:new 實例化對象的時候,讀取或設置一個類的靜態字段(被final修飾、已在編譯期把結果放入常量池的靜態字段除外),以及調用一個類的靜態方法的時候
2.reflect反射調用的時候,如果類沒有初始化需要先觸發其初始化;
3.當初始化一個類時其父類未初始化則需先觸發其父類的初始化;
4.包含main方法的類虛擬機會先初始化這個類;
5.當時用動態語言支持時,解析結果的實例是static相關如果未初始化需要先觸發其初始化。

除此之外,所有引用方式都不會觸發初始化,稱爲被動引用:
1.通過子類引用父類的靜態變量,不會導致子類初始化
2.通過數組定義來引用類,不會觸發此類的初始化
3.常量在編譯階段會存入調用類的常量池中,本質上沒有直接引用到定義常量的類,因此不會觸發定義常量類的初始化

6.使用

7.卸載

三.類加載器

判斷兩個類是否相同,只有在兩個類在同一個類加載器加載的前提下才有意義,否則,即使這兩個類來源於同一個Class文件,被同一個虛擬機加載,只要加載它們的類加載器不同,這兩個類就必定不相同.

即:
在JVM中表示兩個class對象是否爲同一個類對象存在兩個必要條件

  1. 類的完整類名必須一致,包括包名。
  2. 加載這個類的ClassLoader(指ClassLoader實例對象)必須相同。

1.類加載器種類

啓動類加載器(Bootstrap ClassLoader): 加載<JAVA_HOME>/lib/中的代碼,啓動類加載器無法被Java程序直接引用.

擴展類加載器(Extension ClassLoader):加載<JAVA_HOME>/lib/ext中的代碼,開發者可以直接使用該加載器.

應用程序類加載器(Application ClassLoader):一般稱爲系統類加載器,負責加載用戶類路徑上所指定的類庫,開發者可直接使用.

如有必要可自己重新定義.

2.雙親委派

如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是吧這個請求委託給弗雷加載器去完成.因此所有類加載請求最終都應傳送到頂層的啓動類加載器中,只有當父加載器反饋

自己無法完成這個加載請求時(在該加載器的搜索範圍中未找到所需要的類),子加載器纔會自己去嘗試加載.可以有效防止多個加載器加載相同類名的類,保證類的唯一性.
在這裏插入圖片描述
四類加載器之間的關係:

  1. 啓動類加載器,由C++實現,沒有父類。
  2. 拓展類加載器(ExtClassLoader),由Java語言實現,父類加載器爲null
  3. 系統類加載器(AppClassLoader),由Java語言實現,父類加載器爲ExtClassLoader
  4. 自定義類加載器,父類加載器肯定爲AppClassLoader。

3.自定義類加載器

需求場景:

  1. 當class文件不在ClassPath路徑下,默認系統類加載器無法找到該class文件,需要實現一個自定義的ClassLoader來加載特定路徑下的class文件生成class對象。
  2. 當一個class文件是通過網絡傳輸並且可能會進行相應的加密操作時,需要先對class文件進行相應的解密後再加載到JVM內存中,這種情況下也需要編寫自定義的ClassLoader並實現相應的邏輯。
  3. 當需要實現熱部署功能時(一個class文件通過不同的類加載器產生不同class對象從而實現熱部署功能),需要實現自定義ClassLoader的邏輯。

參考:
類的成員變量存在JVM的哪塊內存區域:https://blog.csdn.net/q503385724/article/details/87910929
深入理解Java類加載器(ClassLoader): https://blog.csdn.net/javazejian/article/details/73413292

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