JVM-白话聊一聊JVM类加载和双亲委派机制源码解析
文章目錄
- Java 執行代碼的大致流程
- 類加載loadClass的步驟
- 類加載器和雙親委派機制
- sun.misc.Launcher源碼解析
- Launcher實例化
- Launcher 構造函數
- 雙親委派機制 源碼解析
- 雙親委派過程
- 源碼解析 ClassLoader#loadClass
- 雙親委派機制的優點
- 全盤負責委托機制
Java 執行代碼的大致流程
我們先回顧下Java 執行代碼的大致流程
假設要執行A類的main方法
sun.misc.Launcher.getLauncher() 獲取運行類自己的加載器ClassLoader --> 是AppClassLoader , 通過上圖源碼可知
獲取到ClassLoader后調用loadClass(“A”)方法加載運行的類A
加載完成執行A類的main方法
程序運行結束
JVM銷毀
類加載loadClass的步驟
其中最核心的方法 loadClass ,其實現我們常說的雙親委派機制 ,我們后面展開。
我們先白話一下類加載的幾個步驟
加載 ----> 驗證 ----> 準備 ----> 解析 ----> 初始化 ----> 使用 ----> 卸載
談及比較多的是前五個 ,我們來捋一捋哈 ,不要嘗試死記硬背,嘗試去理解它的邏輯
類加載器和雙親委派機制
剛才說了類加載器中loadClass方法實現了雙親委派的機制,那我們需要先了解下有哪幾種類加載器
主要有4種
我們來看看 幾種不同的類加載器
public class ClassLoadTest {public static void main(String[] args) {// 核心rt.jar中的類加載器 是C++加載的,因此這里為null System.out.println(String.class.getClassLoader());// 擴展包的加載器 ExtClassLoaderSystem.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader());// 應用加載器 AppClassLoaderSystem.out.println(ClassLoadTest.class.getClassLoader());System.out.println("");// 獲取系統ClassLoaderClassLoader appClassLoader = ClassLoader.getSystemClassLoader();// appClassLoader的父加載器ClassLoader extClassLoader = appClassLoader.getParent();// extClassLoader的父加載器ClassLoader boostrapClassLoader = extClassLoader.getParent();System.out.println("the bootstrapLoader : " + boostrapClassLoader);System.out.println("the extClassloader : " + extClassLoader);System.out.println("the appClassLoader : "+ appClassLoader);System.out.println("");System.out.println("==============bootstrapLoader加載的文件====================");URL[] urLs = Launcher.getBootstrapClassPath().getURLs();for (int i = 0; i < urLs.length; i++) {System.out.println(urLs[i]);}System.out.println("");System.out.println("==============extClassloader加載的文件====================");System.out.println(System.getProperty("java.ext.dirs"));System.out.println("");System.out.println("==============appClassLoader 加載的文件====================");System.out.println(System.getProperty("java.class.path"));} }輸出
null sun.misc.Launcher$ExtClassLoader@29453f44 sun.misc.Launcher$AppClassLoader@18b4aac2the bootstrapLoader : null the extClassloader : sun.misc.Launcher$ExtClassLoader@29453f44 the appClassLoader : sun.misc.Launcher$AppClassLoader@18b4aac2==============bootstrapLoader加載的文件==================== file:/E:/Program%20Files/Java/jdk1.8.0_161/jre/lib/resources.jar file:/E:/Program%20Files/Java/jdk1.8.0_161/jre/lib/rt.jar file:/E:/Program%20Files/Java/jdk1.8.0_161/jre/lib/sunrsasign.jar file:/E:/Program%20Files/Java/jdk1.8.0_161/jre/lib/jsse.jar file:/E:/Program%20Files/Java/jdk1.8.0_161/jre/lib/jce.jar file:/E:/Program%20Files/Java/jdk1.8.0_161/jre/lib/charsets.jar file:/E:/Program%20Files/Java/jdk1.8.0_161/jre/lib/jfr.jar file:/E:/Program%20Files/Java/jdk1.8.0_161/jre/classes==============extClassloader加載的文件==================== E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext==============appClassLoader 加載的文件==================== E:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;D:\IdeaProjects\GOF23\target\classes;C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar看看appClassLoader 咋加載這么多? 其實它并沒有加載這么多,除了 D:\IdeaProjects\GOF23\target\classes; 是它加載的,剩下的都是他的父加載器給他干的。
sun.misc.Launcher源碼解析
JVM啟動時,C++會實例化JVM啟動器實例sun.misc.Launcher ,所以很有必要研究一下Launcher的源碼 。
Launcher實例化
private static Launcher launcher = new Launcher();采用了 餓漢模式 靜態域的方式 實現了單例模式 ,保證一個JVM虛擬機內只有一個sun.misc.Launcher實例。
Launcher 構造函數
實例化,調用構造函數,我們看下它的構造函數干了啥?
public Launcher() {Launcher.ExtClassLoader var1;try {//構造擴展類加載器,在構造的過程中將其父加載器設置為nullvar1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}try { //構造應用類加載器,在構造的過程中將其父加載器設置為ExtClassLoader,//Launcher的loader屬性值是AppClassLoader,我們一般都是用這個類加載器來加載我們自己寫的應用程序this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}Thread.currentThread().setContextClassLoader(this.loader);...............}}Launcher構造方法內部, 創建了兩個類加載器,分別是sun.misc.Launcher.ExtClassLoader(擴展類加載器)和sun.misc.Launcher.AppClassLoader(應用類加載器)。
JVM默認使用Launcher的getClassLoader()方法返回的類加載器AppClassLoader的實例加載我們的應用程序。
雙親委派機制 源碼解析
雙親委派過程
通俗的說: 當我們需要加載某個類時會先委托父加載器尋找目標類,找不到再委托上層父加載器加載,如果所有父加載器在自己的加載類路徑下都找不到目標類,則在自己的類加載路徑中查找并載入目標類。
舉個例子,我們有個類A.class ,最先會找應用程序類加載器AppClassLoader 加載,AppClassLoader 會先委托擴展類加載器ExtClassLoader加載,擴展類加載器再委托引導類加載器BootClassLoader,頂層引導類加載器BootClassLoader在自己的類加載路徑里 沒找到A類,則向下退回加載A類的請求,擴展類加載器ExtClassLoader收到回復就自己加載,在自己的類加載路徑里找了半天也沒找到A類,又向下退回A類的加載請求給應用程序類加載器AppClassLoader ,應用程序類加載器 在自己的類加載路徑里找A類,結果找到了就自己加載了。。
源碼解析 ClassLoader#loadClass
loadClass實現了雙親委派的功能,我們有必要好好的研究一下
既然都是委托向上查找,那我們來看下應用程序類加載器AppClassLoader加載類的雙親委派機制源碼,AppClassLoader的loadClass方法最終會調用其父類ClassLoader的loadClass方法
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded// 檢查當前類加載器是否已經加載了該類 ,加載直接返回Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {//如果當前加載器父加載器不為空則委托父加載器加載該類if (parent != null) {c = parent.loadClass(name, false);} else { //如果當前加載器父加載器為空則委托引導類加載器加載該類c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();//調用URLClassLoader的findClass方法在加載器的類路徑里查找并加載該類c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}看注釋~
總結一下幾個步驟
雙親委派機制的優點
全盤負責委托機制
這個比較好理解
“全盤負責”是指當一個ClassLoder裝載一個類時,除非顯示的使用另外一個ClassLoder,該類所依賴及引用的類也由這個ClassLoder載入。
比如我們的類 A中引用了 類B,由于全盤負責委托機制 ,類B也將有加載類A的加載器來加載,除非你顯示的使用另外一個ClassLoder。
總結
以上是生活随笔為你收集整理的JVM-白话聊一聊JVM类加载和双亲委派机制源码解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MyBatis源码-解读Executor
- 下一篇: JVM - 实现自定义的ClassLoa