JVM核心之JVM运行和类加载全过程
為什么研究類(lèi)加載全過(guò)程?
- 有助于連接JVM運(yùn)行過(guò)程
- 更深入了解java動(dòng)態(tài)性(解熱部署,動(dòng)態(tài)加載),提高程序的靈活性
?
類(lèi)加載機(jī)制
- JVM把class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)、解析和初始化,最終形成JVM可以直接使用的java類(lèi)型的全過(guò)程。
?
?
?
- 加載
- 將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),在堆中生成一個(gè)代表這個(gè)類(lèi)的java.lang.Class對(duì)象,作為方法區(qū)類(lèi)數(shù)據(jù)的訪問(wèn)入口,這個(gè)過(guò)程需要類(lèi)加載器參與。
?
?
?
- 鏈接??? 將java類(lèi)的二進(jìn)制代碼合并到JVM的運(yùn)行狀態(tài)之中的過(guò)程
- 驗(yàn)證:確保加載的類(lèi)信息符合JVM規(guī)范,沒(méi)有安全方面的問(wèn)題
-
- 準(zhǔn)備:正式為類(lèi)變量(static變量)分配內(nèi)存并設(shè)置類(lèi)變量初始值的階段,這些內(nèi)存都將在方法去中進(jìn)行分配
-
- 解析:虛擬機(jī)常量池的符號(hào)引用替換為字節(jié)引用過(guò)程
- 初始化
- 初始化階段是執(zhí)行類(lèi)構(gòu)造器<clinit>()方法的過(guò)程。類(lèi)構(gòu)造器<clinit>()方法是由編譯器自動(dòng)收藏類(lèi)中的所有類(lèi)變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊(static塊)中的語(yǔ)句合并產(chǎn)生
- 當(dāng)初始化一個(gè)類(lèi)的時(shí)候,如果發(fā)現(xiàn)其父類(lèi)還沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其父類(lèi)的初始化
- 虛擬機(jī)會(huì)保證一個(gè)類(lèi)的<clinit>()方法在多線程環(huán)境中被正確加鎖和同步
- 當(dāng)范圍一個(gè)Java類(lèi)的靜態(tài)域時(shí),只有真正聲名這個(gè)域的類(lèi)才會(huì)被初始化
?
?
例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)域 fieldstatic{System.out.println("靜態(tài)初始化類(lèi)A");width = 300 ;}public A() {System.out.println("創(chuàng)建A類(lèi)的對(duì)象");} }?
?
分析:
說(shuō)明:
內(nèi)存中存在棧、堆(放創(chuàng)建好的對(duì)象)、方法區(qū)(實(shí)際也是一種特殊堆)
1、JVM加載Demo01時(shí)候,首先在方法區(qū)中形成Demo01類(lèi)對(duì)應(yīng)靜態(tài)數(shù)據(jù)(類(lèi)變量、類(lèi)方法、代碼…),同時(shí)在堆里面也會(huì)形成java.lang.Class對(duì)象(反射對(duì)象),代表Demo01類(lèi),通過(guò)對(duì)象可以訪問(wèn)到類(lèi)二進(jìn)制結(jié)構(gòu)。然后加載變量A類(lèi)信息,同時(shí)也會(huì)在堆里面形成a對(duì)象,代表A類(lèi)。
2、main方法執(zhí)行時(shí)會(huì)在棧里面形成main方法棧幀,一個(gè)方法對(duì)應(yīng)一個(gè)棧幀。如果main方法調(diào)用了別的方法,會(huì)在棧里面挨個(gè)往里壓,main方法里面有個(gè)局部變量A類(lèi)型的a,一開(kāi)始a值為null,通過(guò)new調(diào)用類(lèi)A的構(gòu)造器,棧里面生成A()方法同時(shí)堆里面生成A對(duì)象,然后把A對(duì)象地址付給棧中的a,此時(shí)a擁有A對(duì)象地址。
3、當(dāng)調(diào)用A.width時(shí),調(diào)用方法區(qū)數(shù)據(jù)。
?
當(dāng)類(lèi)被引用的加載,類(lèi)只會(huì)加載一次
- 類(lèi)的主動(dòng)引用(一定會(huì)發(fā)生類(lèi)的初始化)
- new一個(gè)類(lèi)的對(duì)象
- 調(diào)用類(lèi)的靜態(tài)成員(除了final常量)和靜態(tài)方法
- 使用java.lang.reflect包的方法對(duì)類(lèi)進(jìn)行反射調(diào)用
- 當(dāng)虛擬機(jī)啟動(dòng),java Demo01,則一定會(huì)初始化Demo01類(lèi),說(shuō)白了就是先啟動(dòng)main方法所在的類(lèi)
- 當(dāng)初始化一個(gè)類(lèi),如果其父類(lèi)沒(méi)有被初始化,則先初始化它父類(lèi)
- 類(lèi)的被動(dòng)引用(不會(huì)發(fā)生類(lèi)的初始化)
- 當(dāng)訪問(wèn)一個(gè)靜態(tài)域時(shí),只有真正聲名這個(gè)域的類(lèi)才會(huì)被初始化
- 通過(guò)子類(lèi)引用父類(lèi)的靜態(tài)變量,不會(huì)導(dǎo)致子類(lèi)初始化
- 通過(guò)數(shù)組定義類(lèi)的引用,不會(huì)觸發(fā)此類(lèi)初始化
- 引用常量不會(huì)觸發(fā)此類(lèi)的初始化(常量在編譯階段就存入調(diào)用類(lèi)的常量池中了)
- 當(dāng)訪問(wèn)一個(gè)靜態(tài)域時(shí),只有真正聲名這個(gè)域的類(lèi)才會(huì)被初始化
例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"));//主動(dòng)引用 // new A(); // System.out.println(A.width); // Class.forName("com.sinosoft.test.A");//被動(dòng)引用 // System.out.println(A.MAX); // A[] as = new A[10];System.out.println(B.width);//B類(lèi)不會(huì)被加載} }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)域 fieldpublic static final int MAX=100; static {System.out.println("靜態(tài)初始化類(lèi)A");width=300;}public A(){System.out.println("創(chuàng)建A類(lèi)的對(duì)象");} }class A_Father extends Object {static {System.out.println("靜態(tài)初始化A_Father");} }?
?
總結(jié)
以上是生活随笔為你收集整理的JVM核心之JVM运行和类加载全过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 密度仅提升10% 台积电2nm工艺挤牙膏
- 下一篇: ThriftParserError: T