一、類的生命周期
加載 -> 驗證 -> 準(zhǔn)備 -> 解析 -> 初始化 -> 使用 -> 卸載
有且僅有以下情況,JVM必須立即對類進(jìn)行初始化:
- new、getstatic、putstatic、invokestatic
- 使用反射對類進(jìn)行調(diào)用時
- 初始化一個類,發(fā)現(xiàn)其父類還未進(jìn)行初始化,則先初始化父類
(接口初始化,并不要求其父接口初始化,只有在真正使用到父接口(如引用接口中定義的常量)才會初始化;
調(diào)用類中的常量,不會觸發(fā)初始化) - JVM啟動時,會先初始化main方法所在類
注意以下幾點,不會觸發(fā)類的初始化
- 通過數(shù)組引用類,不會觸發(fā)初始化,如:
SomeClass[] someClassArr = new SomeClass[10];
- 常量在編譯階段會存入調(diào)用類的常量池,本質(zhì)上并沒有直接引用到定義類,如:
System.out.println(Constants.HELLO);
二、類的加載過程
- 加載
- 通過類的全限定名,獲取類的二進(jìn)制字節(jié)流
- 將字節(jié)流代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)
- 在方法區(qū)(針對HotSpot而言)生成一個代表此類的java.lang.Class對象。
這個對象將作為程序訪問方法區(qū)中這些類型數(shù)據(jù)的外部接口
- 驗證
其中,符號引用驗證,發(fā)生在JVM將符號引用轉(zhuǎn)化為直接引用的時候(解析階段)。 - 準(zhǔn)備
為類變量(被static修飾的變量)分配內(nèi)存并設(shè)置初始值,這些變量所使用的內(nèi)存,在方法區(qū)分配
特殊情況:如果是常量,則在準(zhǔn)備階段直接賦給定值 - 解析
JVM將常量池內(nèi)的符號引用替換為直接引用的過程,包括以下常量:
- CONSTANT_Class_info
- CONSTANT_Methodref_info
- CONSTANT_Fieldref_info
- CONSTANT_InterfaceMethodref_info
- 初始化
執(zhí)行<cinit>的過程,c指class的意思。
JVM會保證<cinit>只執(zhí)行一次,即便在多線程環(huán)境中
<cinit> 是編譯器自動收集類中所有static變量的賦值動作和static{}, 并按源文件中出現(xiàn)順序形成的
三、類加載器
每一個類加載器,都擁有一個獨立的類命名空間,【命名空間 + 類全限定名】唯一確定一個類。
雙親委派模型
父加載器反饋無法加載時,才由子加載器自己加載
- 避免重復(fù)加載
- 防止JVM類(如Object.class被覆蓋)
- Bootstrap ClassLoader
<JAVA_HOME>/lib下,并且僅按照文件名識別(如rt.jar) - 擴(kuò)展類加載器
<JAVA_HOME>/lib/ext下 - 應(yīng)用程序類加載器
classpath指定路徑