談談 Java 類載入機制
概述
類載入器主要分為兩類,一類是 JDK 預設提供的,一類是使用者自定義的。 JDK 預設提供三種類載入器:
Bootstrap ClassLoader 啟動類載入器:每次執行java命令時都會使用該載入器為虛擬機器載入核心類。該載入器是由native code實現,而不是 Java 程式碼,載入類的路徑為<JAVA_HOME>/jre/lib。特別的<JAVA_HOME>/jre/lib/rt.jar中包含了sun.misc.Launcher類, 而sun.misc.Launcher$ExtClassLoader和sun.misc.Launcher$AppClassLoader都是sun.misc.Launcher的內部類,所以擴充類載入器和系統類載入器都是由啟動類載入器載入的。
Extension ClassLoader, 擴充類載入器:用於載入擴充庫中的類。擴充庫路徑為<JAVA_HOME>/jre/lib/ext/。實現類為sun.misc.Launcher$ExtClassLoader
System ClassLoader系統類載入器:用於載入 CLASSPATH 中的類。實現類為sun.misc.Launcher$AppClassLoader
歡迎學Java和大資料的朋友們加入java架構交流: 855835163
群內提供免費的架構資料還有:Java工程化、高效能及分散式、高效能、深入淺出。高架構。效能調優、Spring,MyBatis,Netty原始碼分析和大資料等多個知識點高階進階乾貨的免費直播講解 可以進來一起學習交流哦
使用者自定義的類載入器
Custom ClassLoader, 一般都是java.lang.ClassLoder的子類
正統的類載入機制是基於雙親委派的,也就是當呼叫類載入器載入類時,首先將載入任務委派給雙親,若雙親無法載入成功時,自己才進行類載入。
在例項化一個新的類載入器時,我們可以為其指定一個parent,即雙親,若未顯式指定,則System ClassLoader就作為預設雙親。
具體的說,類載入任務是由ClassLoader的loadClass()方法來執行的,他會按照以下順序載入類:
通過findLoadedClass()看該類是否已經被載入。該方法為 native code 實現,若已載入則返回。
若未載入則委派給雙親,parent.loadClass(),若成功則返回。
若未成功,則呼叫findClass()方法載入類。java.lang.ClassLoader中該方法只是簡單的丟擲一個ClassNotFoundException所以,自定義的 ClassLoader 都需要 OverridefindClass()方法。
類載入API
java.lang.ClassLoader
ClassLoader是一個抽象類。
待載入的類必須用The Java™ Language Specification定義的全類名,全類名的定義請查閱The Form of a Binary。
給定一個全類名,類載入器應該去定位該類所在的位置。通用的策略是將全類名轉換為類檔案路徑,然後通過類檔案路徑在檔案系統中定位。
每一個載入到記憶體的類都由一個 Class 物件來表示,每一個 Class 物件都有一個指向載入該類的類載入器的引用。但是陣列的 Class 物件是由 Java 執行時環境建立的,通過Class.getClassLoader()方法返回的是陣列元素的類載入器,若陣列元素是基本型別,則返回null,若類是由Bootstrap ClassLoader載入的話也是返回null。
publicclassMain {
publicstaticvoidmain(String[] args) {
// Object 類在 <java_home>/jre/lib/rt.jar 中,
// 由 Bootstrap ClassLoader 載入,由於該類載入器是由 native code 編寫
// 所以輸出為 null
Object[] objects = newObject[5];
System.out.println();
System.out.println(objects.getClass().getClassLoader());
// ZipFileAttributes 類在 <java_home>/jre/lib/ext/zipfs.jar 中,
// 由 Extension ClassLoader 載入,
// 輸出為 sun.misc.Launcher$ExtClassLoader@4b67cf4d
ZipFileAttributes[] attributes = newZipFileAttributes[5];
System.out.println();
System.out.println(attributes.getClass().getClassLoader());
// Main 類是自定義的類,
// 預設由 System ClassLoader 載入,
// 輸出為 sun.misc.Launcher$AppClassLoader@18b4aac2
Main[] array = newMain[5];
array[0] = newMain();
System.out.println();
System.out.println(array.getClass().getClassLoader());
}
}
ClassLoader預設支援並行載入,但是其子類必須呼叫ClassLoader.registerAsParallelCapable()來啟用並行載入
一般來說,JVM 從本地檔案系統載入類的行為是與平臺有關的。
defineClass()方法可以將位元組流轉換成一個Class物件。然後呼叫Class.newInstance()來建立類的例項
java.security.SecureClassLoader
增加了一層許可權驗證,因為關注點不在安全,所以暫不討論。
java.net.URLClassLoader
該類載入器用來載入 URL 指定的 JAR 檔案或目錄中的類和資源,以/結尾的 URL 認為是目錄,否則認為是 JAR 檔案。
// 嘗試通過 URLClassLoader 來載入桌面下的 Test 類。
publicclassMain {
publicstaticvoidmain(String[] args) {
try{
URL[] urls = newURL[1];
URLStreamHandler streamHandler = null;
File classPath = newFile("/home/chen/Desktop/");
String repository = (newURL("file", null,
classPath.getCanonicalPath() + File.separator))
.toString();
urls[0] = newURL(null, repository, streamHandler);
ClassLoader loader = newURLClassLoader(urls);
Class testClass = loader.loadClass("Test");
// output: java.net.URLClassLoader@7f31245a
System.out.println(testClass.getClassLoader());
} catch(MalformedURLException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
} catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Tomcat 8.5.15類載入機制
Tomcat 使用正統的類載入機制(雙親委派),但部分地方做了改動。
Bootstrap classLoader 和 Extension classLoader 的作用不變。
System classLoader正常情況下載入的是CLASSPATH下的類,但是 Tomcat 的啟動指令碼並未使用該變數,而是從以下倉庫下載入類:
$CATALINA_HOME/bin/bootstrap.jar包含了 Tomcat 的啟動類。在該啟動類中建立了Common classLoader、Catalina classLoader、shared classLoader。因為$CATALINA_BASE/conf/catalina.properties中只對common.loader屬性做了定義,server.loader和shared.loader屬性為空,所以預設情況下,這三個 classLoader 都是CommonLoader。具體的程式碼邏輯可以查閱org.apache.catalina.startup.Bootstrap類的initClassLoaders()方法和createClassLoader()方法。
$CATALINA_BASE/bin/tomcat-juli.jar包含了 Tomcat 日誌模組所需要的實現類。
$CATALINA_HOME/bin/commons-daemon.jar。
Common classLoader 是位於 Tomcat 應用伺服器頂層的公用類載入器。由其載入的類可以由 Tomcat 自身類和所有應用程式使用。掃描路徑由 $CATALINA_BASE/conf/catalina.properties 檔案中的 common.loader 屬性定義。預設是 $CATALINA_HOME/lib。
catalina classLoader 用於載入伺服器內部可見類,這些類應用程式不能訪問。
shared classLoader 用於載入應用程式共享類,這些類伺服器不會依賴。
Webapp classLoader 。每個應用程式都會有一個獨一無二的 webapp classloader,他用來載入本應用程式 /WEB-INF/classes 和 /WEB-INF/lib 下的類。
特別的:
Webapp classLoader的預設行為會與正常的雙親委派模式不同:
從Bootstrap classloader載入。
若沒有,從/WEB-INF/classes載入。
若沒有,從/WEB-INF/lib/*.jar載入。
若沒有,則依次從System、Common、shared載入(該步驟使用雙親委派)。
當然了,我們也可以通過配置來使Webapp classLoader嚴格按照雙親委派模式載入類:
通過在工程的META-INF/context.xml(和WEB-INF/classes在同一目錄下) 配置檔案中新增<Loader delegate="true"/>
因為Webapp classLoader的實現類是org.apache.catalina.loader.WebappLoader,他有一個屬性叫delegate, 用來控制類載入器的載入行為,預設為false,我們可以使用set方法,將其設為true來啟用嚴格雙親委派載入模式。
嚴格雙親委派模式載入步驟:
從Bootstrap classloader載入。
若沒有,則依次從System、Common、shared載入。
若沒有,從/WEB-INF/classes載入。
若沒有,從/WEB-INF/lib/*.jar載入。
歡迎學Java和大資料的朋友們加入java架構交流: 855835163
群內提供免費的架構資料還有:Java工程化、高效能及分散式、高效能、深入淺出。高架構。效能調優、Spring,MyBatis,Netty原始碼分析和大資料等多個知識點高階進階乾貨的免費直播講解 可以進來一起學習交流哦
相關文章
- 大白話談JVM的類載入機制JVM
- 從Java的類載入機制談起:聊聊Java中如何實現熱部署(熱載入)Java熱部署
- java類載入機制Java
- Java類載入機制總結Java
- Java基礎篇—Java類載入機制Java
- java虛擬機器類載入機制Java虛擬機
- Java 虛擬機器類載入機制Java虛擬機
- Java基礎-類載入器以及載入機制Java
- Java類載入機制-雙親委派Java
- Java 技術之類載入機制Java
- 淺談Java —— Reflection機制(一)Java
- 類載入機制
- Java虛擬機器(六):類載入機制Java虛擬機
- Java 虛擬機器之四:Java類載入機制Java虛擬機
- Java安全基礎之Java反射機制和ClassLoader類載入機制Java反射
- 談談JavaScript中的this機制JavaScript
- 談談 JVM 垃圾回收機制JVM
- [譯]談談SpringBoot 事件機制Spring Boot事件
- Java類載入機制詳解【java面試題】Java面試題
- 虛擬機器類載入機制:類載入時機虛擬機
- JVM(三)-java虛擬機器類載入機制JVMJava虛擬機
- 類的載入機制
- JVM:類載入機制JVM
- JVM類載入機制JVM
- java基礎(一):談談java記憶體管理與垃圾回收機制Java記憶體
- JVM 第三篇:Java 類載入機制JVMJava
- 一文學會 Java 類載入機制Java
- 談談ConcurrentHashMap的擴容機制HashMap
- 淺談Java的反射機制和作用Java反射
- 探祕類載入器和類載入機制
- Java高階篇——深入淺出Java類載入機制Java
- Java面試題之Java類載入機制詳解!Java面試題
- 深入理解Java虛擬機器 - 類載入機制Java虛擬機
- 深入理解Java虛擬機器(類載入機制)Java虛擬機
- 深入理解Java虛擬機器 --- 類載入機制Java虛擬機
- 【Java虛擬機器規範】JVM類載入機制Java虛擬機JVM
- 類載入機制總結
- 類載入機制與反射反射