日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【JVM】jvm的双亲委派机制

發布時間:2023/12/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【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的双亲委派机制的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。