JVM核心教程之JVM運(yùn)行與類加載全過程詳解
為什么要使用類加載器?
Java語言里,類加載都是在程序運(yùn)行期間完成的,這種策略雖然會令類加載時(shí)稍微增加一些性能開銷,但是會給java應(yīng)用程序提供高度的靈活性。例如:
1.編寫一個(gè)面向接口的應(yīng)用程序,可能等到運(yùn)行時(shí)再指定其實(shí)現(xiàn)的子類;
2.用戶可以自定義一個(gè)類加載器,讓程序在運(yùn)行時(shí)從網(wǎng)絡(luò)或其他地方加載一個(gè)二進(jìn)制流作為程序代碼的一部分;(這個(gè)是Android插件化,動態(tài)安裝更新apk的基礎(chǔ))
為什么研究類加載全過程?
- 有助于連接JVM運(yùn)行過程
- 更深入了解java動態(tài)性(解熱部署,動態(tài)加載),提高程序的靈活性
類加載機(jī)制
JVM把class文件加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校驗(yàn)、解析和初始化,最終形成JVM可以直接使用的java類型的全過程。
加載
將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),在堆中生成一個(gè)代表這個(gè)類的java.lang.Class
對象,作為方法區(qū)類數(shù)據(jù)的訪問入口,這個(gè)過程需要類加載器參與。
鏈接
將java類的二進(jìn)制代碼合并到JVM的運(yùn)行狀態(tài)之中的過程
- 驗(yàn)證: 確保加載的類信息符合JVM規(guī)范,沒有安全方面的問題
- 準(zhǔn)備: 正式為類變量(static變量)分配內(nèi)存并設(shè)置類變量初始值的階段,這些內(nèi)存都將在方法去中進(jìn)行分配
- 解析: 虛擬機(jī)常量池的符號引用替換為字節(jié)引用過程
初始化
- 初始化階段是執(zhí)行類構(gòu)造器
<clinit>()
方法的過程。類構(gòu)造器<clinit>()
方法是由編譯器自動收藏類中的所有類變量的賦值動作和靜態(tài)語句塊(static塊)中的語句合并產(chǎn)生 - 當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化,則需要先觸發(fā)其父類的初始化
- 虛擬機(jī)會保證一個(gè)類的
<clinit>()
方法在多線程環(huán)境中被正確加鎖和同步 - 當(dāng)范圍一個(gè)Java類的靜態(tài)域時(shí),只有真正聲名這個(gè)域的類才會被初始化
例1:
public class Demo01 { public static void main(String[] args) { A a = new A(); System.out.println(a.width); } } class A{ public static int width=100; //靜態(tài)變量,靜態(tài)域 field static{ System.out.println("靜態(tài)初始化類A"); width = 300 ; } public A() { System.out.println("創(chuàng)建A類的對象"); } }
分析:
說明:
內(nèi)存中存在棧、堆(放創(chuàng)建好的對象)、方法區(qū)(實(shí)際也是一種特殊堆)
1、JVM加載Demo01時(shí)候,首先在方法區(qū)中形成Demo01類對應(yīng)靜態(tài)數(shù)據(jù)(類變量、類方法、代碼…),同時(shí)在堆里面也會形成java.lang.Class
對象(反射對象),代表Demo01類,通過對象可以訪問到類二進(jìn)制結(jié)構(gòu)。然后加載變量A類信息,同時(shí)也會在堆里面形成a對象,代表A類。
2、main方法執(zhí)行時(shí)會在棧里面形成main方法棧幀,一個(gè)方法對應(yīng)一個(gè)棧幀。如果main方法調(diào)用了別的方法,會在棧里面挨個(gè)往里壓,main方法里面有個(gè)局部變量A類型的a,一開始a值為null,通過new調(diào)用類A的構(gòu)造器,棧里面生成A()方法同時(shí)堆里面生成A對象,然后把A對象地址付給棧中的a,此時(shí)a擁有A對象地址。
3、當(dāng)調(diào)用A.width時(shí),調(diào)用方法區(qū)數(shù)據(jù)。
當(dāng)類被引用的加載,類只會加載一次
類的主動引用(一定會發(fā)生類的初始化)
- new一個(gè)類的對象
- 調(diào)用類的靜態(tài)成員(除了final常量)和靜態(tài)方法
- 使用
java.lang.reflect
包的方法對類進(jìn)行反射調(diào)用 - 當(dāng)虛擬機(jī)啟動,java Demo01,則一定會初始化Demo01類,說白了就是先啟動main方法所在的類
- 當(dāng)初始化一個(gè)類,如果其父類沒有被初始化,則先初始化它父類
類的被動引用(不會發(fā)生類的初始化)
- 當(dāng)訪問一個(gè)靜態(tài)域時(shí),只有真正聲名這個(gè)域的類才會被初始化
- 通過子類引用父類的靜態(tài)變量,不會導(dǎo)致子類初始化
- 通過數(shù)組定義類的引用,不會觸發(fā)此類初始化
- 引用常量不會觸發(fā)此類的初始化(常量在編譯階段就存入調(diào)用類的常量池中了)
例2:
public class Demo01 { static{ System.out.println("靜態(tài)初始化Demo01"); } public static void main(String[] args) throws Exception { System.out.println("Demo01的main方法!"); System.out.println(System.getProperty("java.class.path")); //主動引用 // new A(); // System.out.println(A.width); // Class.forName("com.sinosoft.test.A"); //被動引用 // System.out.println(A.MAX); // A[] as = new A[10]; System.out.println(B.width);//B類不會被加載 } } class B extends A { static { System.out.println("靜態(tài)初始化B"); } } class A extends A_Father { public static int width=100; //靜態(tài)變量,靜態(tài)域 field public static final int MAX=100; static { System.out.println("靜態(tài)初始化類A"); width=300; } public A(){ System.out.println("創(chuàng)建A類的對象"); } } class A_Father extends Object { static { System.out.println("靜態(tài)初始化A_Father"); } }
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
- JVM中ClassLoader類加載器的深入理解
- jvm運(yùn)行原理以及類加載器實(shí)例詳解
- 淺談Java自定義類加載器及JVM自帶的類加載器之間的交互關(guān)系
- 通俗講解JVM的類加載機(jī)制
- jvm之java類加載機(jī)制和類加載器(ClassLoader)的用法
- JVM類加載機(jī)制原理及用法解析
- 深入理解JVM之類加載機(jī)制詳解
- 一文讀懂Jvm類加載機(jī)制
- JVM的類加載過程以及雙親委派模型詳解
- 詳解JVM類加載機(jī)制及類緩存問題的處理方法
- jvm類加載器基礎(chǔ)解析
- 淺談JVM核心之JVM運(yùn)行和類加載
- JVM類加載機(jī)制詳解
- Java虛擬機(jī)之類加載
相關(guān)文章
SpringBoot+Security 發(fā)送短信驗(yàn)證碼的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot+Security 發(fā)送短信驗(yàn)證碼的實(shí)現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05java編程無向圖結(jié)構(gòu)的存儲及DFS操作代碼詳解
這篇文章主要介紹了java編程無向圖結(jié)構(gòu)的存儲及DFS操作代碼詳解,具有一定借鑒價(jià)值,需要的朋友可以了解下。2017-12-12SpringSecurity怎樣使用注解控制權(quán)限
這篇文章主要介紹了SpringSecurity怎樣使用注解控制權(quán)限的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06SpringMvc MultipartFile實(shí)現(xiàn)圖片文件上傳示例
本篇文章主要介紹了SpringMvc MultipartFile實(shí)現(xiàn)圖片文件上傳示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02詳解分別用Kotlin和java寫RecyclerView的示例
本篇文章主要介紹了詳解分別用Kotlin和java寫RecyclerView的示例,詳解分別用Kotlin和java寫RecyclerView的示例2017-12-12幾種常見的Java運(yùn)行時(shí)異常小結(jié)
在Java編程語言中異常處理是一項(xiàng)關(guān)鍵的機(jī)制,它幫助開發(fā)者識別和修復(fù)程序運(yùn)行時(shí)可能出現(xiàn)的問題,下面這篇文章主要給大家介紹了幾種常見的Java運(yùn)行時(shí)異常的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08Java基于面向?qū)ο髮?shí)現(xiàn)一個(gè)戰(zhàn)士小游戲
這篇文章主要為大家詳細(xì)介紹了Java如何基于面向?qū)ο髮?shí)現(xiàn)一個(gè)戰(zhàn)士小游戲,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以動手嘗試一下2022-07-07Springboot配置文件內(nèi)容加密代碼實(shí)例
這篇文章主要介紹了Springboot配置文件內(nèi)容加密代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11JAVA對象中使用?static?和?String?基礎(chǔ)探究
這篇文章主要介紹了JAVA對象中使用static和String基礎(chǔ)探究,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09java方法重寫和super關(guān)鍵字實(shí)例詳解
這篇文章主要介紹了java方法重寫和super關(guān)鍵字實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03