JDK源碼解析之 java.lang.ClassLoader

Class代表它的作用對象是類,Loader代表它的功能是加載,那么ClassLoader就是把一個以.class結尾的文件以JVM能識別的存儲形式加載到內存中。

一、核心方法

1、loadClass方法

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) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();//findClass需要Overridec = 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;}}

一開始,先調用findLoadedClass(name)去檢查該類是否已經被加載過了,加載過的類不會重復加載。如果是第一次加載(向上傳遞任務),分為兩種:1、parent != null ;當前類加載器不是BootStrapClassLoader,直接調用父類加載器的loadClass來加載該類。2、parent == null 當前加載器是BootStrapClassLoader,則自己嘗試加載該類。

如果上層的加載器都沒有成功加載。(該類沒有在上面任何類加載器的類命名空間下。)任務向下委托,最終回到最初接到加載任務的類加載器,調用findClass()來嘗試加載該類。如果我們自定義一個ClassLoader,可以直接覆蓋該方法(findClass)來進行定制加載邏輯

可以看出,只要每次優先調用parent.loadClass()來執行加載邏輯,就實現了雙親委派模型,同時發現,想破壞雙親委派模型也可以,覆蓋loadClass方法,并且里面不調用parent.loadClass方法即可。

二、總結

JVM啟動時產生三個類加載器:Bootstrap ClassLoader,Extension ClassLoader,App ClassLoader。

Bootstrap ClassLoader負責加載核心類庫(jdk lib下的類),用C++實現,java代碼中顯示為null;Extension ClassLoader負責加載lib/ext下的類;App ClassLoader負責加載ClassPath下的類。

其中Bootstrap ClassLoader是Extention ClassLoader的父加載器,Extention ClassLoader是APP ClassLoader的父加載器,但不是父子類關系。 由源代碼可見雙親委托機制在加載類時類似遞歸先回溯到Bootstrap ClassLoader,再Extension ClassLoader,再App ClassLoader…此機制一個重要原因是安全原因:防止不安全類的加載進來,比如重新實現String類Boolean類等,加載時回溯到Bootstrap ClassLoader會發現已經加載過了,在不會加載自定義的String類取代掉JDK String。判斷兩個類是不是同一個類,除了名字相同還要是加載器類相同才可以。

三、拓展

1、類加載過程

一個類在被使用之前,會經歷class文件生成—>加載—>連接—>初始化等階段。這些階段組合起來為完整的類加載過程,其中加載階段主要完成三件事:

  1. 通過類的全限定名來獲取定義次類的二進制字節流
  2. 將該二進制字節流定義的靜態數據結構轉換成方法區的運行時數據結構
  3. 在內存中生成一個代表該類的Class對象,供外部通過該對象來獲取類的元數據信息

上述類加載的大部分階段是由JVM控制的,但JVM對于加載階段有些沒有做強制限制,比如從哪獲取class文件,以及如何加載class都可以由用戶自定義實現方案,正是由于JVM提供的這些入口,衍生出了動態字節碼,applet, OSGI,類隔離(ali-Pandora)等技術。

正常情況下一個ClassLoader需要兩個必要屬性

1、parent:用于指明當前ClassLoader的父類加載器
2、url:類命名空間,用于指明當前ClassLoader從哪里加載class文件

2、ClassLoader分類:

1、系統自帶的ClassLoader
2、用戶自定義類加載ClassLoader

其中系統自帶的ClassLoader分成三類:
1.1、 BootstrapClassloader: 啟動類加載器, 加載JAVA_HOME/lib/目錄下的所有jar包, 而該目錄下的主要放系統核心類庫,比如包含Object, String等類的rt.jar就是由該類加載器加載進內存的.
1.2、ExtClassloader: 擴展類加載器, 加載JAVA_HOME/jre/lib/ext/目錄下的所有jar包或者由java.ext.dirs系統屬性指定的jar包。放入這個目錄下的jar包對所有AppClassloader都是可見的(雙親委派模型 ).
1.3、AppClassloader | SystemClassLoader: 應用類加載器, 也叫系統類加載器. 是我們平時接觸最密切的類加載器, 主要負責加載classpath下的所有類. 平時我們用maven構建項目時, 添加進pom文件中的依賴, 最后都是由idea幫我們直接把依賴關聯的jar包放到classPath下, 并在運行時, 由AppClassloader幫我們把這些jar包中的class加載到內存中.

總結下來,BootstrapClassloader和ExtClassLoader主要負責加載JDK相關的系統類。AppClassLoader來處理我們classpath下的所有類。

3、疑問:既然Class使用前需要ClassLoader加載到內存,那ClassLoader本身是由誰加載的?

BootStrapClassLoader并不是用java寫的,而是用C++,他屬于JVM的一部分,在JVM啟動時就會被連帶啟動起來,所以在不需要被某一個ClassLoader加載。ExtClassLoader和AppClassLoader是定義在rt.jar中,所以是有BootStrapClassLoader加載進來的。

4、疑問:一個類什么時候被加載:

簡單來說,程序第一次使用某個類的時候,就會調用ClassLoader去加載該類。比如:new 一個類的實例對象或者使用反射Class.forName(…)來獲取Class對象(類對象)都會觸發類加載。

5、疑問:ClassLoader有很多種,我們僅知道他們都有各自的加載路徑,而它們是什么關系,如何協作配合的呢?(雙親委派模型是什么):

雙親委派模型圖

要理解雙親委派模型,首先要了解各個類加載器的層次關系。如圖,從下往上看,每個類的parent是在它上方與它相鄰的類加載器,比如最下面的CustomClassLoader代表用戶自定義類加載器,它的parent是AppClassLoader,AppClassLoader的parent是ExtClassLoader,ExtClassLoader的parent是BootStrapClassLoader,這種遞推關系到BootStrapClassLoader終止,因為它是頂層類加載器,它沒有parent。

注意:這種層次關系是組合形式來實現的,并不是傳統理解的繼承,如:CustomClassLoader的parent是AppClassLoader,但CustomClassLoader并不是AppClassLoader的子類,僅僅是把自己類中的parent屬性指向AppClassLoader

6、疑問:當需要加載一個類是,各個類加載器它們是如何協同工作的呢?

當一個類加載器想要加載一個類時,它會先把該加載任務委托給它的父類加載器(parent),而不是自己先嘗試加載,以此類推,父類加載器又會委托自己的父類加載器去執行加載任務,直到最頂層的BootStrapClassLoader為止,如果BootStrapClassLoader在自己類空間(上面提到的URL)找到了該類的Class文件,就會加載該類到內存中,如果找不到,BootStrapClassLoader會把任務向下傳遞回ExtClassLoader,讓它去嘗試加載該類,如果ExtClassLoader在自己的類空間中找到該類的class文件則會加載該類到內存中,如果找不到,則ExtClassLoader會把任務繼續向下傳遞,以此類推,直到某個類加載器在自己的類命名空間中找到該類的Class文件,并加載到內存中

可以看出,從下往上傳遞任務,秉著優先讓上層加載器去加載的原則,而從上到下傳遞任務,秉著優先讓自己去加載的原則

好處:可以讓每個類具有優先級層次,越靠上方的類加載器加載進來的類層級就會越高,在程序中可見的范圍就會越大。如Object類,因為處于rt.jar中,根據雙親委派模型的執行規律,會被最上面的BootStrapClassLoader加載進來,也就能保證該類在程序中只會被加載一次,也只會存在一份,所有的程序使用的都是這一份,不會出現自己定義一個Object類,被自己的ClassLoader給加載進來的情況,因為rt.jar中的Object會被優先加載。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/535735.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/535735.shtml
英文地址,請注明出處:http://en.pswp.cn/news/535735.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux Vi的使用

一、插入文本┌──┬────────────┐│命令│描述 │├──┼────────────┤│i │在當前字符前插入文本 │├──┼────────────┤│I │在行首插入文本 │├──┼────────────┤│a │在當前字符后添加文本 │├──┼──…

Hive-beeline服務

Hive客戶端工具后續使用了Beeline 替代HiveCLI &#xff0c;并且后續版本也會廢棄掉HiveCLI 客戶端工具,Beeline是 Hive 0.11版本引入的新命令行客戶端工具,它是基于SQLLine CLI的JDBC客戶端。 Beeline支持嵌入模式(embedded mode)和遠程模式(remote mode)。在嵌入式模式下&am…

用戶賬號管理基本概念

什么是用戶賬號管理用戶賬號一般包括普通用戶賬號、管理賬號和系統賬號。為了鑒別用戶身份以及加強系統安全&#xff0c;系統為每個使用它的人分配了一個賬號&#xff0c;這就是普通用戶賬號。每個人擁有一個獨立的普通用戶賬號&#xff0c;每個賬號有不同的用戶名和密碼。用戶…

JDK源碼解析之 Java.lang.Compiler

Compiler類提供支持Java到本機代碼編譯器和相關服務。在設計上&#xff0c;它作為一個占位符在JIT編譯器實現。 一、源碼部分 public final class Compiler {private Compiler() {} // dont make instancesprivate static native void initialize();private st…

shell的基本概念

Shell就像一個殼層&#xff0c;這個殼層介于用戶和操作系統之間&#xff0c;負責將用戶的命令解釋為操作系統可以接收的低級語言&#xff0c;并將操作系統響應的信息以用戶可以了解的方式來顯示。 從用戶登陸到注銷期間&#xff0c;用戶輸入的每個命令都會經過解譯及…

JDK源碼解析之 java.lang.System

一個和系統環境進行交互的類. System不允許被實例化, 而且是一個final類 一、不能實例化 private System() { }二、成員變量 public final static InputStream in null; //這是“標準”輸入流。 public final static PrintStream out null; //這是“標準”輸出流。 public …

詳解MySQL中DROP,TRUNCATE 和DELETE的區別

注意:這里說的delete是指不帶where子句的delete語句 相同點: truncate和不帶where子句的delete, 以及drop都會刪除表內的數據 不同點: 1. truncate和 delete只刪除數據不刪除表的結構(定義) drop語句將刪除表的結構被依賴的約束(constrain),觸發器(trigger),索引(index…

JDK源碼解析之 Java.lang.Package

如果我們在Class對象上調用getPackage方法&#xff0c;就可以得到描述該類所在包的Package對象(Package類是在java.lang中定義的)。我們也可以用包名通過調用靜態方法getPackage或者調用靜態方法getPackages(該方法返回由系統中所有已知包構成的數組)來獲得Package對象。getNam…

Mysql中limit的用法詳解

在我們使用查詢語句的時候&#xff0c;經常要返回前幾條或者中間某幾行數據&#xff0c;這個時候怎么辦呢&#xff1f;不用擔心&#xff0c;mysql已經為我們提供了這樣一個功能。SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset LIMIT 子句可以被用于強制 SE…

Docker入門-簡介

獨具魅力的Docker作為一門新技術&#xff0c;它的出現有可能引起其所在領域大范圍的波動甚至是重新洗牌。根據業內專業人士的看法&#xff0c;不論如何&#xff0c;Docker的出現&#xff0c;已經成為云服務市場中一枚極具意義的戰略性棋子。從2013年開始在國內發力&#xff0c;…

Mysql中limit的優化

在一些情況中&#xff0c;當你使用LIMIT row_count而不使用HAVING時&#xff0c;MySQL將以不同方式處理查詢。 如果你用LIMIT只選擇一些行&#xff0c;當MySQL選擇做完整的表掃描時&#xff0c;它將在一些情況下使用索引。 如果你使用LIMIT row_count與ORD…

Docker入門-架構

Docker 包括三個基本概念: 鏡像&#xff08;Image&#xff09;&#xff1a;Docker 鏡像&#xff08;Image&#xff09;&#xff0c;就相當于是一個 root 文件系統。比如官方鏡像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系統的 root 文件系統。容器&#xff08;Cont…

MYSQL出錯代碼列表大全(中文)

mysql出錯了,以前往往靠猜.現在有了這張表,一查就出來了. 1005&#xff1a;創建表失敗1006&#xff1a;創建數據庫失敗1007&#xff1a;數據庫已存在&#xff0c;創建數據庫失敗1008&#xff1a;數據庫不存在&#xff0c;刪除數據庫失敗1009&#xff1a;不能刪除數據庫文件導致…

Docker入門-安裝

Centos7下安裝Docker docker官方說至少Linux 內核3.8 以上&#xff0c;建議3.10以上&#xff08;ubuntu下要linux內核3.8以上&#xff0c; RHEL/Centos 的內核修補過&#xff0c; centos6.5的版本就可以&#xff09; 1、把yum包更新到最新&#xff1a;yum update 2、安裝需要的…

Docker原理之Namespaces

命名空間&#xff08;namespaces&#xff09;是 Linux 為我們提供的用于分離進程樹、網絡接口、掛載點以及進程間通信等資源的方法。 一、Namespaces 在日常使用 Linux 或者 macOS 時&#xff0c;我們并沒有運行多個完全分離的服務器的需要&#xff0c;但是如果我們在服務器上啟…

mysql 快速插入(insert)多條記錄

方法1: INSERT INTO table(col_1, col_2,col_3) VALUES(1,11,111); INSERT INTO table(col_1, col_2,col_3)   VALUES(2,22,222); INSERT INTO table(col_1, col_2,col_3)   VALUES(3,33,333); 有沒有更快捷的辦法呢?答案是有(見方法2) 方法2: INSERT INTO table(col…

Docker原理之CGroups

控制組&#xff08;cgroups&#xff09;是 Linux 內核的一個特性&#xff0c;主要用來對共享資源進行隔離、限制、審計 等。只有能控制分配到容器的資源&#xff0c;才能避免當多個容器同時運行時的對系統資源的競爭。控制組技術最早是由 Google 的程序員 2006 年起提出&#x…

Mysql中的轉義字符

字符串是多個字符組成的一個字符序列&#xff0c;由單引號( “”) 或雙引號 ( “"”) 字符包圍。(但在 ANSI 模式中運行時只能用單引號)。 例如&#xff1a; a string"another string"在一個字符串中&#xff0c;如果某個序列具有特殊的含義&#xff0c;每個序…

Docker原理之UnionFS

一、UnionFS Linux 的命名空間和控制組分別解決了不同資源隔離的問題&#xff0c;前者解決了進程、網絡以及文件系統的隔離&#xff0c;后者實現了 CPU、內存等資源的隔離&#xff0c;但是在 Docker 中還有另一個非常重要的問題需要解決 - 也就是鏡像。 鏡像到底是什么&#…

教你精確編寫高質量高性能的MySQL語法

在應用系統開發初期&#xff0c;由于開發數據庫數據比較少&#xff0c;對于查詢SQL語句&#xff0c;復雜視圖的編寫&#xff0c;剛開始不會體會出SQL語句各種寫法的性能優劣&#xff0c;但是如果將應用系統提交實際應用后&#xff0c;隨著數據庫中數據的增加&#xff0c;系統的…