【JVM】jvm的双亲委派机制
雙親委派機制
- 一、JVM體系結構
- 二、雙親委派機制的含義
- 三、雙親委派機制的源代碼
- 四、雙親委派機制的意義
- 五、示例代碼
一、JVM體系結構
我們先在這里放一張 JVM 的體系架構圖,方便我們有個總體認知。
在了解JVM的雙親委派機制之前,你不得不需要知道的幾個名字:本文我們只講上圖中里的類加載子系統下的三個階段之一(Loading,加載階段)有關的內容,即下圖中用紅色線圈起來的幾個名詞。
引導類加載器(Bootstrap ClassLoader);
擴展類加載器(Extension ClassLoader);
應用類加載器(Application ClassLoader)。
有關其詳細概念請移步至:【JVM】java的jvm類加載器和類加載子系統之JVM類加載器和類加載子系統里的加載階段,在這里就不多說了。
二、雙親委派機制的含義
當一個類加載器收到了類加載的請求的時候,它不會直接去加載指定的類,而是把這個請求委托給自己的父加載器去加載。只有父加載器無法加載這個類的時候,才會由當前這個加載器來負責類的加載。
Java中提供如下四種類型的加載器,每一種加載器都有指定的加載對象,具體如下:
- Bootstrap ClassLoader(引導類加載器) :主要負責加載Java核心類庫,%JAVA_HOME%/jre/lib/目錄下,resources.jar或者sun.boot.class.path路徑下的內容等。
- Extention ClassLoader(擴展類加載器):主要負責從 java.ext.dirs 系統屬性所指定的目錄中加載類庫,或從JDK的安裝目錄的**/jre/lib/ext/**子目錄(擴展目錄)下加載的類庫。
- Application ClassLoader(應用程序類加載器) :主要負責加載當前應用的classpath下的所有類。
- User-Defined ClassLoader(用戶自定義類加載器) : 用戶自定義的類加載器,可加載指定路徑的class文件。
注意:這里存在的加載器之間的層級關系并不是以繼承的方式存在的,而是以組合的方式處理的。
這四種類加載器存在如下關系,當進行類加載的時候,雖然用戶自定義類不會由 Bootstrap ClassLoader 或是 Extension ClassLoader 加載(由類加載器的加載范圍決定),但是代碼實現還是會一直委托到 Bootstrap ClassLoader, 上層無法加載,再由下層是否可以加載,如果都無法加載,就會觸發 findclass(),拋出 classNotFoundException。
三、雙親委派機制的源代碼
打開IDEA等代碼開發工具,搜索 ClassLoader 并進入類中,找到 loadClass() 方法,源代碼如下:
/*** Loads the class with the specified binary name. * This method searches for classes in the same manner as the loadClass(String, boolean) method. * It is invoked by the Java virtual machine to resolve class references. * Invoking this method is equivalent to invoking #loadClass(String, boolean) loadClass(name,false).** @param: name The binary name of the class* @return: The resulting Class object* @throws: ClassNotFoundException If the class was not found*/public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}/*** Loads the class with the specified binary name. * The default implementation of this method searches for classes in the following order:** 1. Invoke #findLoadedClass(String) to check if the class* has already been loaded. * 2. Invoke the #loadClass(String) loadClass method* on the parent class loader. If the parent is null the class* loader built-in to the virtual machine is used, instead. * 3. Invoke the #findClass(String) method to find the* class. * * If the class was found using the above steps, and the* resolve flag is true, this method will then invoke the #resolveClass(Class) method on the resulting Class object.** Subclasses of ClassLoader are encouraged to override * #findClass(String), rather than this method. ** Unless overridden, this method synchronizes on the result of* #getClassLoadingLock getClassLoadingLock method* during the entire class loading process.** @param: name The binary name of the class* @param: resolve If true then resolve the class* @return: The resulting Class object* @throws: ClassNotFoundException If the class could not be found*/protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> 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) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();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;}}其實這段代碼已經很好的解釋了雙親委派機制,為了大家更容易理解,我做了一張圖來描述一下上面這段代碼的流程:
從上圖中我們就更容易理解了,當一個 Hello.class 這樣的文件要被加載時。不考慮我們自定義類加載器,首先會在 AppClassLoader 中檢查是否加載過,如果有那就無需再加載了。如果沒有,那么會拿到父加載器,然后調用父加載器的 loadClass() 方法。父類中同理也會先檢查自己是否已經加載過,如果沒有再往上。注意這個類似遞歸的過程,直到到達 Bootstrap ClassLoader 之前,都是在檢查是否加載過,并不會選擇自己去加載。直到 Bootstrap ClassLoader,已經沒有父加載器了,這時候開始考慮自己是否能加載了,如果自己無法加載,會下沉到子加載器去加載,一直到最底層,如果沒有任何加載器能加載,就會拋出 ClassNotFoundException。那么有人就有下面這種疑問了?
為什么為有這樣的設計?下面我們再來說下這樣設計的意義就明白了這樣做的好處了。
四、雙親委派機制的意義
這種設計有個好處是:
- 第一:避免類的重復加載。
- 第二:保護程序的安全,防止核心API被隨意篡改。
如果有人想替換系統級別的類,比如:String.java。篡改它的實現,在這種機制下這些系統的類已經被Bootstrap ClassLoader 加載過了(因為當一個類需要加載的時候,最先去嘗試加載的就是 Bootstrap ClassLoader),所以其他類加載器并沒有機會再去加載,從一定程度上防止了危險代碼的植入。
五、示例代碼
現在有一個例子,在我們自定義的 String 類中(包名是 java.lang包下創建),寫一個main方法,執行它,如下:
package java.lang;public class String {static {System.out.println("這是自定義的String類!");}public static void main(String[] args) {String customClass = new String();System.out.println(customClass);} }執行后的結果如下:
可以看到,報錯在java.lang.String中找不到類方法。因為String類是啟動類加載器創建的,不是我們自定義的String類,故沒有main方法。
完結!
總結
以上是生活随笔為你收集整理的【JVM】jvm的双亲委派机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python获取编码方式_在Python
- 下一篇: Android kernel中wakeu