原文
https://www.cnblogs.com/malcolmfeng/p/6485604.html
Java ClassLoader 原理分析
一、ClassLoader(類加載器)的作用
如果一個(gè)程序包含不止一個(gè)class文件,那么當(dāng)程序啟動時(shí),帶有main方法的類的class文件作為程序入口先被JVM加載,然后根據(jù)程序調(diào)用的需要,再逐步進(jìn)行其他class文件的加載。ClassLoader的作用就是動態(tài)的加載class文件。
加載的結(jié)果:
在加載class文件之后,會在方法區(qū)中生成“類信息的二進(jìn)制數(shù)據(jù)”,包含靜態(tài)變量、靜態(tài)方法、常量池和類的代碼,同時(shí)在堆中生成一個(gè)Class對象,此對象代表這個(gè)“二進(jìn)制數(shù)據(jù)”。
二、Java默認(rèn)的三個(gè)ClassLoader
1.Bootstrap ClassLoader 啟動類加載器,是java類加載器中最頂層的類加載器,負(fù)責(zé)加載JDK中的核心類庫,可通過以下代碼獲取該類加載器加載了哪些相關(guān)jar或class文件:
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i].toExternalForm());
}
也可以通過以下代碼:
System.out.println(System.getProperty(“sun.boot.class.path"));
ps:(bootstrap ClassLoader不是繼承自此,是C++進(jìn)行編寫的,嵌到了JVM內(nèi)核)。
2.Extension ClassLoader:擴(kuò)展類加載器,負(fù)責(zé)加載jre/lib/ext/目錄下的所有jar。
3.App ClassLoader:系統(tǒng)應(yīng)用類加載器,主要負(fù)責(zé)加載應(yīng)用程序classpath目錄下的所有jar和class文件。
三、雙親委托機(jī)制:
在加載類的過程中,Bootstrap先進(jìn)行加載,如果沒有加載到,則extension再進(jìn)行加載,如果還是沒有加載到,再交給App加載。避免了用戶定義String等核心api中的類。如果同一個(gè)name的class文件,不是由同一個(gè)ClassLoader加載的,JVM也會認(rèn)為這是兩個(gè)不同的class文件。
通過以下代碼打印出所有的類加載器:
ClassLoader classloader= c1.getClassLoader();
while(classloader!=null){
System.out.println(classloader);
classloader = classloader.getParent();
}
System.out.println(classloader);
自定義加載器
除了這三個(gè)類加載器,用戶可以自定義自己的類加載器。既然JVM已經(jīng)提供了默認(rèn)的類加載器,為什么還要定義自已的類加載器呢?
因?yàn)镴ava中提供的默認(rèn)ClassLoader,只加載指定目錄下的jar和class,如果我們想加載其它位置的類或jar時(shí),比如:我要加載網(wǎng)絡(luò)上的一個(gè)class文件,通過動態(tài)加載到內(nèi)存之后,要調(diào)用這個(gè)類中的方法實(shí)現(xiàn)我的業(yè)務(wù)邏輯。在這樣的情況下,默認(rèn)的ClassLoader就不能滿足我們的需求了,所以需要定義自己的ClassLoader。
如何自定義類加載器:
1、繼承java.lang.ClassLoader
2、重寫父類的findClass方法
父類有那么多方法,為什么偏偏只重寫findClass方法?
因?yàn)镴DK已經(jīng)在loadClass方法中幫我們實(shí)現(xiàn)了ClassLoader搜索類的算法,當(dāng)在loadClass方法中搜索不到類時(shí),loadClass方法就會調(diào)用findClass方法來搜索類,所以我們只需重寫該方法即可。如沒有特殊的要求,一般不建議重寫loadClass搜索類的算法。
小測試1:
將ClassLoaderTest.class打包成ClassLoaderTest.jar,放到Extension ClassLoader的加載目錄下(JAVA_HOME/jre/lib/ext),再運(yùn)行代碼,會發(fā)現(xiàn)。。。是由ext加載的。
小測試2:
用Bootstrcp ClassLoader來加載ClassLoaderTest.class,有兩種方式:
1、在jvm中添加-Xbootclasspath參數(shù),指定Bootstrcp ClassLoader加載類的路徑,并追加我們自已的jar(ClassTestLoader.jar)
2、將class文件放到JAVA_HOME/jre/classes/目錄下(上面有提到)
idea執(zhí)行程序前加載的類
// JDK目錄
JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home
// idea目錄
IDEA_HOME = /Applications/IntelliJ IDEA.app/Contents
// 項(xiàng)目目錄
WORK_PATH = /Users/jiaoningbo/Other/learning
// maven倉庫目錄
MAVEN_REPOSITORY = /Users/jiaoningbo/.m2
命令行:執(zhí)行com.reven.common.ClassLoader 類中的main函數(shù)
{JAVA_HOME}/bin/java "
-javaagent
:{IDEA_HOME}/lib/idea_rt.jar=60737
:{IDEA_HOME}/bin"
-Dfile.encoding=UTF-8
-classpath {JAVA_HOME}/jre/lib/charsets.jar
:{JAVA_HOME}/jre/lib/deploy.jar
:{JAVA_HOME}/jre/lib/ext/cldrdata.jar
:{JAVA_HOME}/jre/lib/ext/dnsns.jar
:{JAVA_HOME}/jre/lib/ext/jaccess.jar
:{JAVA_HOME}/jre/lib/ext/jfxrt.jar
:{JAVA_HOME}/jre/lib/ext/localedata.jar
:{JAVA_HOME}/jre/lib/ext/nashorn.jar
:{JAVA_HOME}/jre/lib/ext/sunec.jar
:{JAVA_HOME}/jre/lib/ext/sunjce_provider.jar
:{JAVA_HOME}/jre/lib/ext/sunpkcs11.jar
:{JAVA_HOME}/jre/lib/ext/zipfs.jar
:{JAVA_HOME}/jre/lib/javaws.jar
:{JAVA_HOME}/jre/lib/jce.jar
:{JAVA_HOME}/jre/lib/jfr.jar
:{JAVA_HOME}/jre/lib/jfxswt.jar
:{JAVA_HOME}/jre/lib/jsse.jar
:{JAVA_HOME}/jre/lib/management-agent.jar
:{JAVA_HOME}/jre/lib/plugin.jar
:{JAVA_HOME}/jre/lib/resources.jar
:{JAVA_HOME}/jre/lib/rt.jar:{JAVA_HOME}/lib/ant-javafx.jar
:{JAVA_HOME}/lib/dt.jar:{JAVA_HOME}/lib/javafx-mx.jar
:{JAVA_HOME}/lib/jconsole.jar
:{JAVA_HOME}/lib/packager.jar
:{JAVA_HOME}/lib/sa-jdi.jar
:{JAVA_HOME}/lib/tools.jar
:{WORK_PATH}/target/classes
:{MAVEN_HOME}/repository/junit/junit/4.12/junit-4.12.jar
:{MAVEN_HOME}/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
:{MAVEN_REPOSITORY}/repository/org/jodd/jodd-all/4.2.0/jodd-all-4.2.0.jar
:{MAVEN_REPOSITORY}/repository/cn/hutool/hutool-all/4.0.7/hutool-all-4.0.7.jar
com.reven.common.ClassLoader