前言
近幾個月學習了儒猿技術窩的專欄《從 0 開始帶你成為JVM實戰高手》后,基于課程講解的知識,做了提煉、歸納、擴展,整理出當前文章。
感謝專欄傳授的知識!專欄中從零開始,一步一圖的方式,加上大量真實線上案例講解,讓我收獲很多。
一、java代碼執行過程
.java源代碼編譯成.class字節碼文件
打包成jar或者war包
諸如"jar -jar"之類的命令運行字節碼文件,此時啟動一個JVM進程
jvm運行.class字節碼文件
jvm通過類加載器把編譯好的.class字節碼文件加載到jvm內存中
JVM基于自己的字節碼執行引擎,執行加載到JVM內存里的類
二、JVM類加載機制
可參考:你真的理解JVM類加載的各階段過程(加載、驗證、準備、解析、初始化)嗎? https://blog.csdn.net/qq_33589510/article/details/106877661
示例代碼:
1.類加載到使用的過程
加載-->驗證-->準備-->解析-->初始化-->使用-->卸載
1.1 類加載
什么情況下會去加載一個類?
代碼中用到這個類的時候,會去加載一個類,也就是會從字節碼文件中加載這個類到JVM內存中。
示例
流程:
1.代碼中main()方法的主類會在JVM進程啟動之后被加載到內存中
2.執行main()方法中代碼
3.發現使用了其他類"ReplicaManager",此時會從對應的".class"字節碼文件中加載對應的類到內存里
1.2 驗證
校驗字節碼文件是否符合JVM規范,如果不符合規范,無法執行
1.3 準備
功能:
(1)給類分配一定的內存空間
(2)給類的類的靜態成員變量(static修飾的變量)分配內存空間,并賦予默認的初始值。
上圖示例代碼中,會給類變量"flushInterval"分配內存空間,并賦予默認初始值"0"。
1.4 解析
符號引用替換為直接引用。過程比較復雜,實際工作中很少用到,可以不深入研究。
1.5 初始化
核心階段。
功能:
執行類中靜態成員變量的賦值和static{}靜態代碼塊
什么時候初始化一個類?
(1) "new xxx()”實例化類的對象時,會觸發類的加載到初始化的全過程
(2) 調用一個類的靜態方法
(3) 調用一個類的靜態字段(非編譯期已知的常量) 不太理解
(4) 使用反射調用其對應方法的時候
(5) 包含"main()”方的主類,會立馬初始化
(7) 如果初始化一個類的時候,沒有他的父類還沒有初始化,那必須先初始化他的父類。
不會引起類初始化的場景-被動使用:
這塊比較難理解
參考:JVM Knowledges-類型的初始化階段 https://blog.csdn.net/time_hunter/article/details/13007715
(1) 調用一個類的static final編譯期已知的常量時
執行結果中沒有打印 “MyClass init!"
(2) 通過子類調用父類的static常量
執行結果中沒有打印 “SubClass init!"
(3) 使用類型數組定義
三、類加載器
類加載到初始化的過程都需要用到類加載器
類加載器分類
(1) 啟動類加載器
Bootstrap ClassLoader。負責加載JDK中”lib"目錄中的核心類庫
(2) 擴展類加載器
Extension ClassLoader。負責加載”lib\ext”目錄中的類
(3) 應用程序類加載器
Application ClassLoader。負責加載”ClassPath”環境變量鎖指定路徑中的類。
(4) 自定義類加載器
自定義的類加載器,根據自己的需求加載類
雙親委派機制
JVM類加載器有親自層級結構,如下圖
當某個類加載器需要加載某個.class文件時,它首先把這個任務委托給他的父級類加載器去加載,最終傳導到頂層的類加載器去加載,但是如果父類加載器在自己負責加載的范圍內,沒找到這個類,則會下推權利到自己的子類加載器。
通俗的示例:
假設JVM需要加載”ReplicaManager”類,會有如下加載步驟
(1)應用程序類加載器檢查是否加載過,如果加載過,則無需再加載;如果沒有加載過,則不先自己加載而是直接委派應用程序類加載器會委派給擴展類加載器加載。
(2)擴展類加載器檢查是否加載過,如果加載過,則無需再加載;如果沒有加載過,則不先自己加載而是直接委派他的父級類加載器即啟動類加載器去加載。
(3)啟動類加載器檢查是否加載過,如果加載過,則無需再加載;如果沒有加載過,啟動類加載器嘗試加載類,如果無法加載到,則下放權利給子級即擴展類加載器去加載。
(4)擴展類加載器可以加載到,則加載。如果也無法加載到,則繼續下放權利給他的子級即應用程序類加載器加載,此時類加載器找到了該類,然后把類加載到內存中
(5)如果都無法加載,則會拋出ClassNotFoundException異常
四、雙親委派機制
作用
1.防止重復加載同一個.class。通過委托去向上問一問,加載了就不用再加載一遍,保證數據安全。
2.保證核心.class不能被篡改。通過委托方式,不會去篡改核心.class,即使篡改也不會去加載,即使加載也不會是同一個.class對象了。不同的加載器加載同一個.class也不是同一個Class對象,保證了Class執行安全。
五、思考題:如何對.class文件處理保證不被人拿到后反編譯獲取公司源代碼?
編譯的時候,采用工具對字節碼加密,或者做混淆等處理,在類加載的時候,對加密的類,采用自定義的類加載器進行解密,這樣.class即使獲取了,也無法執行,即保護了源碼。