天天看點

小白從0到1看jvm(2、類加載器與雙親委派機制)

一、概述

網上有許多介紹jvm的文章和視訊,但大多枯燥、晦澀難懂,很難讓小白堅持下去;如果你是一個小白,不用擔心,在本文中,我們将從0到1,一步步地介紹JVM的基本概念和工作原理。無論你是想深入了解Java語言,還是準備參加Java開發的面試,這篇文章都将為你提供有用的知識和技能。讓我們一起開始這段有趣的旅程吧!

小白從0到1看jvm(2、類加載器與雙親委派機制)

二、正文

類加載器

idea編譯後的.class檔案,通過類加載器将之加載到JVM記憶體中,并放在運作時資料區的方法區内,然後再建立相對應的java.lang.Class對象,用來封裝類在方法區内的資料結構。

類加載器種類

類加載器名稱 說明
Bootstrap ClassLoader 【啟動類加載器】

1、采用c/c++實作,嵌套在jvm内部

2、加載Java核心類庫(JAVA_HOME/jre/lib/rt.jar),用于提供JVM自身需要的類

3、無法直接通路

Extension ClassLoader 【擴充類加載器】

1、采用java語言編寫

2、加載(JAVA_HOME/jre/lib/ext)下類庫

3、父類加載器:啟動類加載器

Application ClassLoader【應用程式類加載器】

1、采用java語言編寫

2、加載“classpath”環境變量所指定的路徑中的類,可以了解為加載我們自己寫的Java代碼,以及我的導入的三方Jar包中的代碼

3、父類加載器:擴充類加載器

自定義類加載器 1、繼承ClassLoader類,可定制類的加載方式
小白從0到1看jvm(2、類加載器與雙親委派機制)

類加載時采用了雙親委派模式,什麼是雙親委派模式

當一個類加載器收到類加載的請求時,自己不會立刻去加載這個類,而是把加載請求委派給父類加載器完成,父類加載器也會做相同的處理;當父加載器在自己的搜尋範圍内找不到需要加載的類時,子加載器才會嘗試自己去加載,最後如果任何一個加載器都找不到這個類時,就會抛出:ClassNotFoundException異常。

classloader源碼分析

public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            // 檢查一下指定名稱的類是否已經加載過,如果加載過了,就不需要再加載,直接傳回
            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) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                if (c == null) {
                    long t1 = System.nanoTime();
                  	// 如果都沒有找到,則通過自定義實作的findClass去查找并加載
                    c = findClass(name);
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
          	// 是否需要在加載時進行解析(根據入參指定)
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }           

為什麼需要雙親委派模式

假如沒有雙親委派機制,此時使用者編寫一個和jdk自帶的一模一樣的類(比如:java.lang.Object),并放到了Classpath中,那麼應用程式類加載器就把這個當做Object加載到了記憶體中,進而造成不可預知的後果;而雙親委派機制先一路向上委托,啟動類加載器去找的時候,就把正确的Object加載到了記憶體中,後面再加載使用者編寫的Object類時,是不會加載運作的。

是以:JDK自帶類是無法被覆寫的,而引入第三方的JAR是可以自己定義相同的類來覆寫的。

繼續閱讀