上一篇 <<<javap命令反查匯編指令匯總
下一篇 >>>自定義SPI和熱部署技術(shù)破壞類加載器的雙親委派模式
java是邏輯程序,class是虛擬機(jī)指令程序。
類加載器:將我們class文件讀取到內(nèi)存中阅悍。
class文件的來源
- 自己寫的java源代碼 編譯成class文件 硬盤讀取
- 通過網(wǎng)絡(luò)的方式下載class文件
- War跳纳、Jar 解壓之后都是class文件
- 從數(shù)據(jù)庫中讀取class文件
- Java動(dòng)態(tài)代理模式 反射/cglib 生成代理class文件
類加載過程
1碟狞、加載(Loading)
a讹俊、class字節(jié)碼文件數(shù)據(jù)轉(zhuǎn)換成方法區(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)【字節(jié)碼文件可以是本地,也可以來自于網(wǎng)絡(luò)亚情、光盤摊腋、硬盤等二進(jìn)制】
b沸版、在堆中創(chuàng)建代表類的class對象(注意不是目標(biāo)類對象,用newInstance就可以生成對象)兴蒸,作為方法區(qū)類數(shù)據(jù)的訪問入口视粮,該對象封裝了類在方法區(qū)中的數(shù)據(jù)結(jié)構(gòu),并且向用戶提供了訪問方法區(qū)數(shù)據(jù)結(jié)構(gòu)的接口橙凳,即Java反射的接口蕾殴。
2、鏈接(Linking)
1)岛啸、驗(yàn)證(Verification):確保加載的類信息符合JVM規(guī)范钓觉,沒有安全方面的問題【JVM校驗(yàn)】
a.文件格式驗(yàn)證:主要驗(yàn)證字節(jié)流是否符合Class文件格式規(guī)范,并且能被當(dāng)前的虛擬機(jī)加載處理坚踩。例如:主荡灾,次版本號是否在當(dāng)前虛擬機(jī)處理的范圍之內(nèi)。常量池中是否有不被支持的常量類型瞬铸。指向常量的中的索引值是否存在不存在的常量或不符合類型的常量卧晓。
b.元數(shù)據(jù)驗(yàn)證:對字節(jié)碼描述的信息進(jìn)行語義的分析,分析是否符合java的語言語法的規(guī)范赴捞。
c.字節(jié)碼驗(yàn)證:最重要的驗(yàn)證環(huán)節(jié),分析數(shù)據(jù)流和控制郁稍,確定語義是合法的赦政,符合邏輯的。主要的針對元數(shù)據(jù)驗(yàn)證后對方法體的驗(yàn)證。保證類方法在運(yùn)行時(shí)不會(huì)有危害出現(xiàn)恢着。
d.符號引用驗(yàn)證:主要是針對符號引用轉(zhuǎn)換為直接引用的時(shí)候桐愉,是會(huì)延伸到第三解析階段,主要去確定訪問類型等涉及到引用的情況掰派,主要是要保證引用一定會(huì)被訪問到从诲,不會(huì)出現(xiàn)類等無法訪問的問題。
2)靡羡、準(zhǔn)備(Preparation):正式為類變量(static變量)分配內(nèi)存并設(shè)置類變量初始值的階段系洛,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配【空間分配】
3)、解析(Resolution):虛擬機(jī)常量池的符號引用替換為字節(jié)引用過程【靜態(tài)和常量池引用替換】
3略步、初始化(Initialization)
初始化階段是執(zhí)行類構(gòu)造器<clinit>()方法的過程描扯。
- 在多線程環(huán)境中會(huì)被正確加鎖和同步
- 自動(dòng)收集類中變量的復(fù)制動(dòng)作,以及static塊的語句合并產(chǎn)生趟薄,static從上往下執(zhí)行绽诚,如果父類未初始化,則先初始化父類杭煎。
雙親委派層次結(jié)構(gòu)
a恩够、啟動(dòng)(Bootstrap)類加載器【加載JVM自身的類,由C++實(shí)現(xiàn)羡铲,沒有父類蜂桶,文件名寫死】
Bootstrap啟動(dòng)類加載器只加載包名為java、javax犀勒、sun等開頭的類屎飘,位于<JAVA_HOME>/lib路徑下的核心類庫和-Xbootclasspath參數(shù)指定的路徑下的jar包
/**
* 啟動(dòng)類加載器的職責(zé)(注意最后的classes目錄沒有的話自己創(chuàng)建)
* /Home/jre/lib/resources.jar
* /Home/jre/lib/rt.jar
* /Home/jre/lib/sunrsasign.jar
* /Home/jre/lib/jsse.jar
* /Home/jre/lib/jce.jar
* /Home/jre/lib/charsets.jar
* /Home/jre/lib/jfr.jar
* /Home/jre/classes
*/
public static void bootstrapClassLoader() {
String property = System.getProperty("sun.boot.class.path");
List<String> list = Arrays.asList(property.split(";"));
list.forEach((t) -> {
System.out.println("啟動(dòng)類加載器目錄:" + t);
});
}
b、擴(kuò)展(ExtClassLoader)類加載器【由Java語言實(shí)現(xiàn)贾费,父類加載器為null】
由Java語言實(shí)現(xiàn)的钦购,是Launcher的靜態(tài)內(nèi)部類,它負(fù)責(zé)加載<JAVA_HOME>/lib/ext目錄下或者由系統(tǒng)變量-Djava.ext.dir指定位路徑中的類庫褂萧,開發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類加載器押桃。
/**
* 擴(kuò)展類加載器
* /Home/jre/lib/ext
* /Library/Java/Extensions
* /Network/Library/Java/Extensions
* /System/Library/Java/Extensions
* /usr/lib/java
*/
public static void extClassLoader() {
String property = System.getProperty("java.ext.dirs");
List<String> list = Arrays.asList(property.split(";"));
list.forEach((t) -> {
System.out.println("擴(kuò)展類加載器" + t);
});
}
c、系統(tǒng) (AppClassLoader) 類加載器【由Java語言實(shí)現(xiàn)导犹,父類加載器為ExtClassLoader】
也稱應(yīng)用程序加載器是指唱凯,它負(fù)責(zé)加載classpath路徑,該類加載是程序中默認(rèn)的類加載器谎痢,通過ClassLoader#getSystemClassLoader()方法可以獲取到該類加載器磕昼。
/**
* app 類加載器(除了包含啟動(dòng)類和擴(kuò)展類,還包括自己寫的代碼)
* /target/classes
*/
public static void appClassLoader() {
String property = System.getProperty("java.class.path");
List<String> list = Arrays.asList(property.split(";"));
list.forEach((t) -> {
System.out.println("應(yīng)用類加載器" + t);
});
}
d节猿、自定義類加載器票从,父類加載器肯定為AppClassLoader漫雕。
雙親委派模式原理及好處
原理:
如果一個(gè)類加載器收到了類加載請求,它并不會(huì)自己先去加載峰鄙,而是把這個(gè)請求委托給父類的加載器去執(zhí)行(存在遞歸)浸间,倘若父類加載器無法完成此加載任務(wù),子加載器才會(huì)嘗試自己去加載吟榴,這就是雙親委派模式魁蒜。
好處:
a、避免類的重復(fù)加載吩翻,當(dāng)父親已經(jīng)加載了該類時(shí)兜看,就沒有必要子ClassLoader再加載一次。
b仿野、可以防止核心API庫被隨意篡改铣减,比如java.lang是核心API包,擴(kuò)展時(shí)強(qiáng)制加載將會(huì)報(bào)出如下異常:java.lang.SecurityException: Prohibited package name: java.lang
類加載器的觸發(fā)節(jié)點(diǎn)
1.調(diào)用類的靜態(tài)方法
2.invokeStatic 調(diào)用靜態(tài)方法
3.Main
4.New
5.Class.formname
6.子類初始化一定會(huì)初始化父類
初始化一個(gè)類脚作,一定會(huì)觸發(fā)類加載器
類加載器加載了該類葫哗,但該類不一定完成初始化。
類加載器效果測試
// 當(dāng)前classLoader【正城蛱危】:sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(JaryeEntity.class.getClassLoader());
// 擴(kuò)展classloader:sun.misc.Launcher$ExtClassLoader@5f8ed237
System.out.println(JaryeEntity.class.getClassLoader().getParent());
// 啟動(dòng)classLoader:null(因?yàn)榈讓邮荂語言寫的)
System.out.println(JaryeEntity.class.getClassLoader().getParent().getParent());
// 把class文件放到啟動(dòng)類的/Home/jre/classes目錄下劣针,則打印結(jié)果為null
System.out.println(JaryeEntity.class.getClassLoader());
// Object在lang包下,屬于啟動(dòng)類加載亿扁,結(jié)果為null
Object o = new Object();
System.out.println(o.getClass().getClassLoader());
雙親委派源碼解讀
ClassLoader.getSystemClassLoader().loadClass("com.mysql.jdbc.Driver")
類加載器核心方法
a捺典、loadClass(String)
ClassLoader類自己實(shí)現(xiàn)的,不再建議用戶重寫但用戶可以直接調(diào)用該方法
加載時(shí)從緩存中獲取从祝,沒有則委派給啟動(dòng)類加載襟己,如果仍然未找到,則調(diào)用findClass()方法去加載牍陌。
運(yùn)行時(shí)加載自己指定的類擎浴,那么我們可以直接使用this.getClass().getClassLoder.loadClass("className"),這樣就可以直接調(diào)用ClassLoader的loadClass方法獲取到class對象毒涧。
b贮预、findClass(String)
JDK1.2之后建議把自定義的類加載邏輯寫在findClass()方法中,在loadClass()方法中被調(diào)用的契讲,這樣就可以保證自定義的類加載器符合雙親委托模式
ClassLoader類中并沒有實(shí)現(xiàn)findClass()方法的具體代碼邏輯仿吞,取而代之的是拋出ClassNotFoundException異常
findClass方法通常是和defineClass方法一起使用的
c、defineClass(byte[] b, int off, int len)
defineClass()方法是用來將byte字節(jié)流解析成JVM能夠識別的Class對象(ClassLoader中已實(shí)現(xiàn)該方法邏輯)
在自定義類加載器時(shí)捡偏,會(huì)直接覆蓋ClassLoader的findClass()方法并編寫加載規(guī)則唤冈,取得要加載類的字節(jié)碼后轉(zhuǎn)換成流,然后調(diào)用defineClass()方法生成類的Class對象
d银伟、resolveClass(Class??? c)
使用該方法可以使用類的Class對象創(chuàng)建完成也同時(shí)被解析你虹。
前面我們說鏈接階段主要是對字節(jié)碼進(jìn)行驗(yàn)證凉当,為類變量分配內(nèi)存并設(shè)置初始值同時(shí)將字節(jié)碼文件中的符號引用轉(zhuǎn)換為直接引用。
相關(guān)文章鏈接:
<<<JVM整體內(nèi)存結(jié)構(gòu)的圖解售葡,直觀明了
<<<javap命令查看對象信息及操作方法在JVM層的實(shí)現(xiàn)原理
<<<javap命令反查匯編指令匯總
<<<自定義SPI和熱部署技術(shù)破壞類加載器的雙親委派模式
<<<JVM中對象如何完成空間分配和初始化工作
<<<JVM元空間(方法區(qū))和棧內(nèi)存溢出原因及解決方案
<<<JVM堆內(nèi)存溢出和內(nèi)存泄露問題定位和解決
<<<JVM常見死鎖問題產(chǎn)生原因和多種診斷方式
<<<服務(wù)器CPU飆升為100%問題排查及如何避免
<<<JVM內(nèi)存診斷命令和排查工具匯總
<<<JVM新生代老年代算法匯總圖解
<<<JVM垃圾回收不要手動(dòng)System.gc的真正原因
<<<JVM垃圾回收引用計(jì)數(shù)法和根搜索算法圖解
<<<JVM垃圾回收STW(Stop-The-World)代碼演示
<<<JVM垃圾回收器的發(fā)展歷程及使用場景匯總
<<<JVM串行并行垃圾回收器的關(guān)注點(diǎn)
<<<一張圖看懂CMS垃圾回收器的底層原理
<<<G1能作為JDK9默認(rèn)垃圾回收器的優(yōu)勢分析
<<<CMS和G1的漏標(biāo)問題解決及三色標(biāo)記算法圖解
<<<GC中新生代進(jìn)入老年代的方式匯總
<<<GC常用日志參數(shù)配置及分析工具說明
<<<FullGC、MinorGC忠藤、STW等常見問題如何解答
<<<JVM性能調(diào)優(yōu)的評估指標(biāo)及調(diào)優(yōu)示例