雙親委派模型,類的載入機制,搞定大廠高頻面試題
看過這篇文章,大廠面試你「雙親委派模型」,硬氣的說一句,你怕啥?
讀該文章姿勢
- 開啟手頭的 IDE,按照文章內容及思路進行程式碼跟蹤與思考
- 手頭沒有 IDE,先收藏,回頭看 (萬一哪次面試問了呢)
- 需要檢視和拷貝程式碼,點選文章末尾出「閱讀原文」
文章內容相對較長,所以添加了目錄,如果你希望對 Java 的類載入過程有個更深入的瞭解,同時增加自己的面試技能點,請耐心讀完......
雙親委派模型
在介紹這個Java技術點之前,先試著思考以下幾個問題:
- 為什麼我們不能定義同名的 String 的 java 檔案?
- 多執行緒的情況下,類的載入為什麼不會出現重複載入的情況?
- 熱部署的原理是什麼?
- 下面程式碼,虛擬機器是怎樣初始化註冊 Mysql 連線驅動(Driver)的?
想理解以上幾個問題的前提是瞭解類載入時機與過程, 這篇文章將會以非常詳細的解讀方式來回答以上幾個問題
類載入時機與過程
類從被載入到虛擬機器記憶體中開始,到卸載出記憶體為止,它的整個生命週期包括:載入(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和解除安裝(Unloading)7個階段。其中準備、驗證、解析3個部分統稱為連線(Linking)。如圖所示
載入、驗證、準備、初始化和解除安裝這5個階段的順序是確定的,類的載入過程必須按照這種順序按部就班地開始,而解析階段則不一定:它在某些情況下可以在初始化階段之後再開始,這是為了支援Java語言的執行時繫結(也稱為動態繫結或晚期繫結)
載入
在載入階段(可以參考java.lang.ClassLoader的loadClass()方法),虛擬機器需要完成以下3件事情:
- 通過一個類的全限定名來獲取定義此類的二進位制位元組流(並沒有指明要從一個Class檔案中獲取,可以從其他渠道,譬如:網路、動態生成、資料庫等);
- 將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構;
- 在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的各種資料的訪問入口;
載入階段和連線階段(Linking)的部分內容(如一部分位元組碼檔案格式驗證動作)是交叉進行的,載入階段尚未完成,連線階段可能已經開始,但這些夾在載入階段之中進行的動作,仍然屬於連線階段的內容,這兩個階段的開始時間仍然保持著固定的先後順序。
驗證
驗證是連線階段的第一步,這一階段的目的是為了確保Class檔案的位元組流中包含的資訊符合當前虛擬機器的要求,並且不會危害虛擬機器自身的安全。 驗證階段大致會完成4個階段的檢驗動作:
- 檔案格式驗證:驗證位元組流是否符合Class檔案格式的規範;例如:是否以魔術0xCAFEBABE開頭(當class檔案以二進位制形式開啟,會看到這個檔案頭,cafebabe)、主次版本號是否在當前虛擬機器的處理範圍之內、常量池中的常量是否有不被支援的型別。
- 元資料驗證:對位元組碼描述的資訊進行語義分析(注意:對比javac編譯階段的語義分析),以保證其描述的資訊符合Java語言規範的要求;例如:這個類是否有父類,除了java.lang.Object之外。
- 位元組碼驗證:通過資料流和控制流分析,確定程式語義是合法的、符合邏輯的。
- 符號引用驗證:確保解析動作能正確執行。
驗證階段是非常重要的,但不是必須的,它對程式執行期沒有影響,如果所引用的類經過反覆驗證,那麼可以考慮採用-Xverifynone引數來關閉大部分的類驗證措施,以縮短虛擬機器類載入的時間。
準備
準備階段是正式為類變數分配記憶體並設定類變數初始值的階段,這些變數所使用的記憶體都將在方法區中進行分配。這時候進行記憶體分配的僅包括類變數(被static修飾的變數),而不包括例項變數,例項變數將會在物件例項化時隨著物件一起分配在堆中。其次,這裡所說的初始值通常情況下是資料型別的零值,假設一個類變數的定義為:
有通常情況就有特殊情況,這裡的特殊是指:
解析
解析階段是虛擬機器將常量池內的符號引用替換為直接引用的過程。解析動作主要針對類或介面、欄位、類方法、介面方法、方法型別、方法控制代碼和呼叫點限定符7類符號引用進行。
初始化
在介紹初始化時,要先介紹兩個方法:<clinit>
和 <init>
:
- 在編譯生成class檔案時,會自動產生兩個方法,一個是類的初始化方法<clinit>, 另一個是例項的初始化方法<init>
clinit>
:在jvm第一次載入class檔案時呼叫,包括靜態變數初始化語句和靜態塊的執行<init>
: 在例項創建出來的時候呼叫,包括呼叫new操作符;呼叫 Class 或 Java.lang.reflect.Constructor 物件的newInstance()方法;呼叫任何現有物件的clone()方法;通過 java.io.ObjectInputStream 類的getObject() 方法反序列化。
類初始化階段是類載入過程的最後一步,到了初始化階段,才真正開始執行類中定義的java程式程式碼。在準備極端,變數已經付過一次系統要求的初始值,而在初始化階段,則根據程式猿通過程式制定的主管計劃去初始化類變數和其他資源,或者說:初始化階段是執行類構造器<clinit>()
方法的過程.
<clinit>()
方法是由編譯器自動收集類中的所有類變數的賦值動作和靜態語句塊 static{} 中的語句合併產生的,編譯器收集的順序是由語句在原始檔中出現的順序所決定的,靜態語句塊只能訪問到定義在靜態語句塊之前的變數,定義在它之後的變數,在前面的靜態語句塊可以賦值,但是不能訪問。如下:
那麼去掉報錯的那句,改成下面:
輸出結果:1
為什麼輸出結果是 1,在準備階段我們知道 i=0,然後類初始化階段按照順序執行,首先執行 static 塊中的 i=0,接著執行 static賦值操作i=1, 最後在 main 方法中獲取 i 的值為1
<clinit>
()方法與例項構造器<init>()
方法不同,它不需要顯示地呼叫父類構造器,虛擬機器會保證在子類<init>()
方法執行之前,父類的<clinit>()
方法方法已經執行完畢- 由於父類的
<clinit>()
方法先執行,也就意味著父類中定義的靜態語句塊要優先於子類的變數賦值操作。 <clinit>()
方法對於類或者介面來說並不是必需的,如果一個類中沒有靜態語句塊,也沒有對變數的賦值操作,那麼編譯器可以不為這個類生產<clinit>()
方法。- 介面中不能使用靜態語句塊,但仍然有變數初始化的賦值操作,因此介面與類一樣都會生成
<clinit>()
方法。但介面與類不同的是,執行介面的<clinit>()
方法不需要先執行父介面的<clinit>()
方法。只有當父介面中定義的變數使用時,父接口才會初始化。另外,介面的實現類在初始化時也一樣不會執行介面的<clinit>()
方法。 - 虛擬機器會保證一個類的
<clinit>()
方法在多執行緒環境中被正確的加鎖、同步,如果多個執行緒同時去初始化一個類,那麼只會有一個執行緒去執行這個類的<clinit>()
方法,其他執行緒都需要阻塞等待,直到活動執行緒執行<clinit>()
方法完畢。如果在一個類的<clinit>()
方法中有耗時很長的操作,就可能造成多個執行緒阻塞,在實際應用中這種阻塞往往是隱藏的。
讓我們來驗證上面的載入規則
驗證 1: 虛擬機器會保證在子類
<init>()
方法執行之前,父類的<clinit>()
方法方法已經執行完畢
輸出結果
SSClass
SuperClass init!
123
**驗證 2: **通過陣列定義來引用類,不會觸發此類的初始化(我的理解是陣列的父類是Object)
輸出結果:無
**驗證 3: **常量在編譯階段會存入呼叫類的常量池中,本質上並沒有直接引用到定義常量的類,因此不會觸發定義常量的類的初始化
輸出結果:
hello world
驗證小結
虛擬機器規範嚴格規定了有且只有5中情況(jdk1.7)必須對類進行“初始化”(而載入、驗證、準備自然需要在此之前開始):
- 遇到 new, getstatic, putstatic, invokestatic 這些位元組碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。生成這4條指令的最常見的Java程式碼場景是:使用new關鍵字例項化物件的時候、讀取或設定一個類的靜態欄位(被final修飾、已在編譯器把結果放入常量池的靜態欄位除外)的時候,以及呼叫一個類的靜態方法的時候。
- 使用 java.lang.reflect 包的方法對類進行反射呼叫的時候,如果類沒有進行過初始化,則需要先觸發其初始化。
- 當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。
- 當虛擬機器啟動時,使用者需要指定一個要執行的主類(包含main()方法的那個類),虛擬機器會先初始化這個主類。
- 當使用jdk1.7動態語言支援時,如果一個 java.lang.invoke.MethodHandle 例項最後的解析結果REF_getstatic, REF_putstatic, REF_invokeStatic 的方法控制代碼,並且這個方法控制代碼所對應的類沒有進行初始化,則需要先出觸發其初始化。
有了這個載入規則的印象,雙親委派模型就很好理解了,彆著急,繼續向下看, 你會發現你的理解層面提高了
雙親委派模型
剛看到這個詞彙的時候我是完全懵懂的狀態,其實就是定義了 JVM 啟動的時候類的載入規則, 大家要按規矩辦事,好辦事,來看下圖:
所謂雙親委派是指每次收到類載入請求時,先將請求委派給父類載入器完成(所有載入請求最終會委派到頂層的Bootstrap ClassLoader載入器中),如果父類載入器無法完成這個載入(該載入器的搜尋範圍中沒有找到對應的類),子類嘗試自己載入, 如果都沒載入到,則會丟擲 ClassNotFoundException 異常, 看到這裡其實就解釋了文章開頭提出的第一個問題,父載入器已經載入了JDK 中的 String.class 檔案,所以我們不能定義同名的 String java 檔案。
為什麼會有這樣的規矩設定?
因為這樣可以避免重複載入,當父親已經載入了該類的時候,就沒有必要 ClassLoader 再載入一次。考慮到安全因素,我們試想一下,如果不使用這種委託模式,那我們就可以隨時使用自定義的String來動態替代java核心api中定義的型別,這樣會存在非常大的安全隱患,而雙親委託的方式,就可以避免這種情況,因為String 已經在啟動時就被引導類載入器(Bootstrcp ClassLoader)載入,所以使用者自定義的ClassLoader永遠也無法載入一個自己寫的String,除非你改變 JDK 中 ClassLoader 搜尋類的預設演算法。
我們發現除了啟動類載入器(BootStrap ClassLoader),每個類都有其"父類"載入器
⚠️ 其實這裡的父子關係是組合模式,不是繼承關係來實現
從圖中可以看到類 AppClassLoader 和 ExtClassLoader 都繼承 URLClassLoader, 而 URLClassLoader 又繼承 ClassLoader, 在 ClassLoader 中有一個屬性
在通過建構函式例項化 AppClassLoader 和 ExtClassLoader 的時候都要傳入一個 classloader 作為當前 classloader 的 parent
頂層ClassLoader有幾個函式很關鍵,先有個印象
指定保護域(protectionDomain),把ByteBuffer的內容轉換成 Java 類,這個方法被宣告為final的
把位元組陣列 b中的內容轉換成 Java 類,其開始偏移為off,這個方法被宣告為final的
查詢指定名稱的類
連結指定的類
類載入器責任範圍
上面我們提到每個載入器都有對應的載入搜尋範圍
- Bootstrap ClassLoader:這個載入器不是一個Java類,而是由底層的c++實現,負責在虛擬機器啟動時載入Jdk核心類庫(如:rt.jar、resources.jar、charsets.jar等)以及載入後兩個類載入器。這個ClassLoader完全是JVM自己控制的,需要載入哪個類,怎麼載入都是由JVM自己控制,別人也訪問不到這個類
- Extension ClassLoader:是一個普通的Java類,繼承自ClassLoader類,負責載入{JAVA_HOME}/jre/lib/ext/目錄下的所有jar包。
- App ClassLoader:是Extension ClassLoader的子物件,負責載入應用程式classpath目錄下的所有jar和class檔案。
大家自行執行這個檔案,就可以看到每個類載入器載入的檔案了
兩種類的載入方式
通常用這兩種方式來動態載入一個 java 類,Class.forName() 與 ClassLoader.loadClass() 但是兩個方法之間也是有一些細微的差別
Class.forName() 方式
檢視Class類的具體實現可知,實質上這個方法是呼叫原生的方法:
形式上類似於Class.forName(name,true,currentLoader)。 綜上所述,Class.forName 如果呼叫成功會:
- 保證一個Java類被有效得載入到記憶體中;
- 類預設會被初始化,即執行內部的靜態塊程式碼以及保證靜態屬性被初始化;
- 預設會使用當前的類載入器來載入對應的類。
ClassLoader.loadClass方式
如果採用這種方式的類載入策略,由於雙親託管模型的存在,最終都會將類的載入任務交付給Bootstrap ClassLoader進行載入。跟蹤原始碼,最終會呼叫原生方法:
與此同時,與上一種方式的最本質的不同是,類不會被初始化,只有顯式呼叫才會進行初始化。綜上所述,ClassLoader.loadClass 如果呼叫成功會:
- 類會被載入到記憶體中;
- 類不會被初始化,只有在之後被第一次呼叫時類才會被初始化;
- 之所以採用這種方式的類載入,是提供一種靈活度,可以根據自身的需求繼承ClassLoader類實現一個自定義的類載入器實現類的載入。(很多開源Web專案中都有這種情況,比如tomcat,struct2,jboss。原因是根據Java Servlet規範的要求,既要Web應用自己的類的優先順序要高於Web容器提供的類,但同時又要保證Java的核心類不被任意覆蓋,此時重寫一個類載入器就很必要了)
雙親委派模型原始碼分析
Launcher
分析類載入器原始碼要從 sun.misc.Launcher.class 檔案看起, 關鍵程式碼已添加註釋,同時可以在此類中看到 ExtClassLoader 和 AppClassLoader 的定義,也驗證了我們上文提到的他們不是繼承關係,而是通過指定 parent 屬性來形成的組合模型
進入上面第25行的 loadClass 方法中
我們看到方法有同步塊(synchronized), 這也就解釋了文章開頭第2個問題,多執行緒情況不會出現重複載入的情況。同時會詢問parent classloader是否有載入,如果沒有,自己嘗試載入。
URLClassLoader中的 findClass方法:
借用網友的一個載入時序圖來解釋整個過程更加清晰:
雙親委派模型的破壞
Java本身有一套資源管理服務JNDI,是放置在rt.jar中,由啟動類載入器載入的。以對資料庫管理JDBC為例,java給資料庫操作提供了一個Driver介面:
然後提供了一個DriverManager來管理這些Driver的具體實現:
這裡省略了大部分程式碼,可以看到我們使用資料庫驅動前必須先要在DriverManager中使用registerDriver()註冊,然後我們才能正常使用。
不破壞雙親委派模型的情況(不使用JNDI服務)
我們看下mysql的驅動是如何被載入的:
核心就是這句Class.forName()觸發了mysql驅動的載入,我們看下mysql對Driver介面的實現:
可以看到,Class.forName()其實觸發了靜態程式碼塊,然後向DriverManager中註冊了一個mysql的Driver實現。這個時候,我們通過DriverManager去獲取connection的時候只要遍歷當前所有Driver實現,然後選擇一個建立連線就可以了。
###破壞雙親委派模型的情況
在JDBC4.0以後,開始支援使用spi的方式來註冊這個Driver,具體做法就是在mysql的jar包中的META-INF/services/java.sql.Driver 檔案中指明當前使用的Driver是哪個,然後使用的時候就直接這樣就可以了:
可以看到這裡直接獲取連線,省去了上面的Class.forName()註冊過程。 現在,我們分析下看使用了這種spi服務的模式原本的過程是怎樣的:
- 第一,從META-INF/services/java.sql.Driver檔案中獲取具體的實現類名“com.mysql.jdbc.Driver”
- 第二,載入這個類,這裡肯定只能用class.forName("com.mysql.jdbc.Driver")來載入
好了,問題來了,Class.forName()載入用的是呼叫者的Classloader,這個呼叫者DriverManager是在rt.jar中的,ClassLoader是啟動類載入器,而com.mysql.jdbc.Driver肯定不在<JAVA_HOME>/lib下,所以肯定是無法載入mysql中的這個類的。這就是雙親委派模型的侷限性了,父級載入器無法載入子級類載入器路徑中的類。
那麼,這個問題如何解決呢?按照目前情況來分析,這個mysql的drvier只有應用類載入器能載入,那麼我們只要在啟動類載入器中有方法獲取應用程式類載入器,然後通過它去載入就可以了。這就是所謂的執行緒上下文載入器。
文章前半段提到執行緒上下文類載入器可以通過 Thread.setContextClassLoaser() 方法設定,如果不特殊設定會從父類繼承,一般預設使用的是應用程式類載入器
很明顯,執行緒上下文類載入器讓父級類載入器能通過呼叫子級類載入器來載入類,這打破了雙親委派模型的原則
現在我們看下DriverManager是如何使用執行緒上下文類載入器去載入第三方jar包中的Driver類的,先來看原始碼:
使用時,我們直接呼叫DriverManager.getConnection() 方法自然會觸發靜態程式碼塊的執行,開始載入驅動然後我們看下ServiceLoader.load()的具體實現:
繼續向下看建構函式例項化 ServiceLoader 做了哪些事情:
檢視 reload() 函式:
繼續檢視LazyIterator構造器,該類同樣實現了Iterator介面:
例項化到這裡我們也將上下文得到的類載入器例項化到這裡,來回看ServiceLoader 重寫的 iterator() 方法:
上面next() 方法呼叫了lookupIterator.next(),這個lookupIterator 就是剛剛例項化的 LazyIterator(); 來看next方法
繼續檢視nextService 方法:
終於到這裡了,在上面 nextService函式中第8行呼叫了c = Class.forName(cn, false, loader) 方法,我們成功的做到了通過執行緒上下文類載入器拿到了應用程式類載入器(或者自定義的然後塞到執行緒上下文中的),同時我們也查詢到了廠商在子級的jar包中註冊的驅動具體實現類名,這樣我們就可以成功的在rt.jar包中的DriverManager中成功的載入了放在第三方應用程式包中的類了同時在第16行完成Driver的例項化,等同於new Driver(); 文章開頭的問題在理解到這裡也迎刃而解了
JAVA熱部署實現
首先談一下何為熱部署(hotswap),熱部署是在不重啟 Java 虛擬機器的前提下,能自動偵測到 class 檔案的變化,更新執行時 class 的行為。Java 類是通過 Java 虛擬機器載入的,某個類的 class 檔案在被 classloader 載入後,會生成對應的 Class 物件,之後就可以建立該類的例項。預設的虛擬機器行為只會在啟動時載入類,如果後期有一個類需要更新的話,單純替換編譯的 class 檔案,Java 虛擬機器是不會更新正在執行的 class。如果要實現熱部署,最根本的方式是修改虛擬機器的原始碼,改變 classloader 的載入行為,使虛擬機器能監聽 class 檔案的更新,重新載入 class 檔案,這樣的行為破壞性很大,為後續的 JVM 升級埋下了一個大坑。
另一種友好的方法是建立自己的 classloader 來載入需要監聽的 class,這樣就能控制類載入的時機,從而實現熱部署。
熱部署步驟:
-
銷燬自定義classloader(被該載入器載入的class也會自動解除安裝);
-
更新class
-
使用新的ClassLoader去載入class
JVM中的Class只有滿足以下三個條件,才能被GC回收,也就是該Class被解除安裝(unload):
- 該類所有的例項都已經被GC,也就是JVM中不存在該Class的任何例項。
- 載入該類的ClassLoader已經被GC。
- 該類的java.lang.Class 物件沒有在任何地方被引用,如不能在任何地方通過反射訪問該類的方法
自定義類載入器
要建立使用者自己的類載入器,只需要繼承java.lang.ClassLoader類,然後覆蓋它的findClass(String name)方法即可,即指明如何獲取類的位元組碼流。
如果要符合雙親委派規範,則重寫findClass方法(使用者自定義類載入邏輯);要破壞的話,重寫loadClass方法(雙親委派的具體邏輯實現)。
感謝與參考
非常感謝以下博文的作者,通過反覆拜讀來了解雙親委派模型的原理
- https://blog.csdn.net/u014634338/article/details/81434327
- https://www.cnblogs.com/aspirant/p/7200523.html
- https://www.cnblogs.com/gdpuzxs/p/7044963.html
- https://www.jianshu.com/p/09f73af48a98
- https://www.cnblogs.com/yahokuma/p/3668138.html
推薦閱讀
- 面試還不知道 BeanFactory 和 ApplicationContext 的區別?
- Spring Bean 生命週期之"我從哪裡來?",懂得這個很重要
- Spring Bean 生命週期之"我要到哪裡去?"
- 如何設計好的RESTful API
- 輕鬆高效玩轉DTO(Data Transfer Object)
後續會出一系列文章點亮上圖,同時進行 Spring 知識點解釋與串聯,在工作中充分利用 Spring 的特性 另外,還會推出 Java 多執行緒與 ElasticSearch 相關內容
歡迎持續關注公眾號:「日拱一兵」
- 前沿 Java 技術乾貨分享
- 高效工具彙總
- 面試問題分析與解答
- 技術資料領取
持續關注,帶你像讀偵探小說一樣輕鬆趣味學習 Java 技術棧相關知識
看過這篇文章,大廠面試你「雙親委派模型」,硬氣的說一句,你怕啥?
讀該文章姿勢
開啟手頭的 IDE,按照文章內容及思路進行程
雙親委派模型下,在父類載入器無法載入的情況下再由當前類載入器去載入。具體的實現邏輯在java.util.ClassLoader抽象類的loadClass方法中。在該方法中,先檢查是否已經載入過,如果沒有,就讓父類載入器載入。如果父類載入器服務載入,就呼叫findClass方法載入。findC
第十六章:多執行緒1、一般而言,程序包含如下3個特徵:獨立性,動態性,併發性。併發性和並行性是兩個概念,並行指同一時刻,有多條指令在多個處理器上同時執行;併發指同一時刻只能有一條指令執行,但多個程序指令 一、引言二、類的載入、連結、初始化1、載入1.1、載入的class來源2、類的連結2.1、驗證2.2、準備2.3、解析3、類的初始化3.1、< clinit>方法相關3.2、類初始化時機3.3、final定義的初始化3.4、ClassLoader只會對類進行載入,不會進行初始化三、類載入器1、JV 前言
一個月沒更新了,這個月發生了太多的事情,導致更新的頻率大大降低,不管怎樣收拾心情,技術的研究不能落下!
jvm作為每個java程式猿必須瞭解的知識,博主推薦一本書《深入理解Java虛擬機器》,以前博主在學校的時候看過幾遍,每一次看都有新的理解。加上工作了也有一年多的時間了,有必要好好總結一番~
什麼是j springboot的啟動都是從main方法開始的,如下:@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.cl
1. 為什麼說Java是一門平臺無關語言?
平臺無關實際的含義是“一次編寫到處執行”。Java 能夠做到是因為它的位元組碼(byte code)可以執行在任何作業系統上,與底層系統無關。
2. 為什麼 Java 不是100%面向物件?
Java
1. JVM背景知識
1995年,Java誕生於Sun公司。目標:Write Once, Run Anywhere。
2006年,Sun宣佈Java開源,並在隨後1年,陸續將JDK的各部分在GPL v2協議下公開原始碼,並建立OpenJDK組織,對原始
---------------------- ASP.Net+Android+IO開發S、.Net培訓、期待與您交流! ----------------------
/*設計模式:
Singleton: 單例模式
Factorty: 工廠模式
Iterator: 迭代器模
一、類載入
1.什麼是類載入?
JVM將編譯好的.class檔案(位元組碼檔案)以二進位制流的方式載入到我們記憶體中,並且將二進位制流中靜態的資料結構轉換成我們方法區中動態執行資料結構,並且在對堆記憶體生成一個java.lang.class物件,作為提供給外界訪問我們方法
概述
概念
虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接時候用的Java型別。
類的生命週期
類從被載入到虛擬機器記憶體中開始,到卸載出記憶體為止,它的整個生命週期包括:載入、驗證、準備、解析、初
目錄
今天我要問你的問題是,請介紹類載入過程,什麼是雙親委派模型?
典型回答
考點分析
知識擴充套件
通常類載入機制有三個基本特徵:
類載入器,類檔案容器等都發生了非常大的變化,我這裡總結一下:
談到類載入器,繞不過的一個話題是自定義類載入器,常見的場景有:
類的生命週期:
載入、連線(包括驗證、準備、解析)、初始化、使用、解除安裝
載入:
1、將.class檔案的二進位制資料讀入記憶體中,並放入執行時資料區的方法區。
2、堆區建立一個java.lang.Class物件,用來封裝類在方法區內的的資料結構,並提供訪問
1、通過一個類的全限定名(包名與類名)來獲取定義此類的二進位制位元組流(Class檔案)。而獲取的方式,可以通過jar包、war包、網路中獲取、JSP檔案生成等方式。
2、將這個位元組流所代表
# 一、類檔案的結構
我們都知道,各種不同平臺的虛擬機器,都支援 “位元組碼 Byte Code” 這種程式儲存格式,這構成了 Java 平臺無關性的基石。甚至現在平臺無關性也開始演變出 “語言無關性” ,就是其他語言也可以執行在 Java 虛擬機器之上,比如現在的 Kotlin、Scala 等。
JVM總括四-類載入過程、雙親委派模型、物件例項化
一、 類載入過程
一定要注意每個過程執行的內容!!!!!!
1、Load:
將編譯後的.class檔案以二進位制流的方式載入到JVM記憶體中,並轉化為特定的資料結構,用到的就是classLoad二類載入器。這個過程中校驗cafe babe JVM總括四-類載入過程、雙親委派模型、物件例項化過程
目錄:JVM總括:目錄
一、 類載入過程
類載入過程就是將.class檔案轉化為Class物件,類例項化的過程,(User user = new User(); 這個過程是物件例項化的過程);
一個.class檔案只有一個Class物件(位元 JVM思考-init和clinit區別
目錄:JVM總括:目錄
clinit和init的區別其實也就是Class物件初始化物件初始化的區別,詳情看我上一篇部落格:
JVM總括四-類載入過程、雙親委派模型、物件例項化過程
一、init和clinit方法執行時機不同
init是物件構
JVM總括四-類載入過程、雙親委派模型、物件例項化過程
目錄:JVM總括:目錄
一、 類載入過程
類載入過程就是將.class檔案轉化為Class物件, 類例項化 的過程 ,(User user = new User(); 這個過程是 物件例項化 的
目錄
Java虛擬機器的記憶體結構:
類載入器圖:
雙親委託模式:
堆記憶體:
GC解析圖:
GC演算法
Java虛擬機器的記憶體結構:
類載入器圖:
雙親委託模式:
Java允許建立和JDK自帶類 相關推薦
雙親委派模型,類的載入機制,搞定大廠高頻面試題
分析JVM雙親委派模型的類載入原始碼 自定義類載入器
《瘋狂Java講義》讀書筆記(十):多執行緒,網路程式設計,類載入機制與反射
類載入流程,類載入機制及自定義類載入器詳解(面試再也不怕了)
關於Jvm類載入機制,這一篇就夠了
Springboot原始碼深度解析,方法解析,類載入解析,容器建立
Java面試題:面向物件,類載入器,JDBC, Spring 基礎概念
Java虛擬機器結構(記憶體,類載入器,執行引擎)
黑馬程式設計師--java高新技術 26--javaBean,泛型,類載入器,代理spring小框架
九、請介紹類載入過程,什麼是雙親委派模型?
類載入機制-雙親委派,破壞雙親委派--這一篇全瞭解
23、請介紹類載入過程,什麼是雙親委派模型?
類生命週期、載入機制、雙親委派模型
JVM類載入機制詳解(二)類載入器與雙親委派模型
類檔案的結構、JVM 的類載入過程、類載入機制、類載入器、雙親委派模型
JVM總括四-類載入過程、雙親委派模型、物件例項化
JVM總括四-類載入過程、雙親委派模型、物件例項化過程 JVM思考-init和clinit區別
JVM思考-init和clinit區別 JVM總括四-類載入過程、雙親委派模型、物件例項化過程
JVM總括四-類載入過程、雙親委派模型、物件例項化過程
深入淺出JVM(jvm記憶體結構,類載入器圖,雙親委託模式,堆記憶體,GC解析,GC演算法)