偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

分析Java類加載全過(guò)程

開(kāi)發(fā) 后端
一個(gè)Java文件從被加載到被卸載這個(gè)生命過(guò)程,總共要經(jīng)歷4個(gè)階段,那么是哪4個(gè)階段呢?下面作者將詳細(xì)的給網(wǎng)友解答……

今天去涉獵了一下類的加載的過(guò)程,現(xiàn)在也總結(jié)一下:

一個(gè)java文件從被加載到被卸載這個(gè)生命過(guò)程,總共要經(jīng)歷4個(gè)階段:

加載->鏈接(驗(yàn)證+準(zhǔn)備+解析)->初始化(使用前的準(zhǔn)備)->使用->卸載

其中加載(除了自定義加載)+鏈接的過(guò)程是完全由jvm負(fù)責(zé)的,什么時(shí)候要對(duì)類進(jìn)行初始化工作(加載+鏈接在此之前已經(jīng)完成了),jvm有嚴(yán)格的規(guī)定(四種情況):

1.遇到new,getstatic,putstatic,invokestatic這4條字節(jié)碼指令時(shí),加入類還沒(méi)進(jìn)行初始化,則馬上對(duì)其進(jìn)行初始化工作。其實(shí)就是3種情況:用new實(shí)例化一個(gè)類時(shí)、讀取或者設(shè)置類的靜態(tài)字段時(shí)(不包括被final修飾的靜態(tài)字段,因?yàn)樗麄円呀?jīng)被塞進(jìn)常量池了)、以及執(zhí)行靜態(tài)方法的時(shí)候。

2.使用java.lang.reflect.*的方法對(duì)類進(jìn)行反射調(diào)用的時(shí)候,如果類還沒(méi)有進(jìn)行過(guò)初始化,馬上對(duì)其進(jìn)行。

3.初始化一個(gè)類的時(shí)候,如果他的父親還沒(méi)有被初始化,則先去初始化其父親。

4.當(dāng)jvm啟動(dòng)時(shí),用戶需要指定一個(gè)要執(zhí)行的主類(包含static void main(String[] args)的那個(gè)類),則jvm會(huì)先去初始化這個(gè)類。

以上4種預(yù)處理稱為對(duì)一個(gè)類進(jìn)行主動(dòng)的引用,其余的其他情況,稱為被動(dòng)引用,都不會(huì)觸發(fā)類的初始化。下面也舉了些被動(dòng)引用的例子:

  1. /**  
  2.  * 被動(dòng)引用情景1  
  3.  * 通過(guò)子類引用父類的靜態(tài)字段,不會(huì)導(dǎo)致子類的初始化  
  4.  * @author volador  
  5.  *  
  6.  */ 
  7. class SuperClass{  
  8.     static{  
  9.         System.out.println("super class init.");  
  10.     }  
  11.     public static int value=123;  
  12. }  
  13.  
  14. class SubClass extends SuperClass{  
  15.     static{  
  16.         System.out.println("sub class init.");  
  17.     }  
  18. }  
  19.  
  20. public class test{  
  21.     public static void main(String[]args){  
  22.         System.out.println(SubClass.value);  
  23.     }  
  24.       

輸出結(jié)果是:super class init。

  1. /**  
  2.  * 被動(dòng)引用情景2  
  3.  * 通過(guò)數(shù)組引用來(lái)引用類,不會(huì)觸發(fā)此類的初始化  
  4.  * @author volador  
  5.  *  
  6.  */ 
  7. public class test{  
  8.     public static void main(String[] args){  
  9.         SuperClass s_list=new SuperClass[10];  
  10.     }  

輸出結(jié)果:沒(méi)輸出

  1. /**  
  2.  * 被動(dòng)引用情景3  
  3.  * 常量在編譯階段會(huì)被存入調(diào)用類的常量池中,本質(zhì)上并沒(méi)有引用到定義常量類類,所以自然不會(huì)觸發(fā)定義常量的類的初始化  
  4.  * @author root  
  5.  *  
  6.  */ 
  7. class ConstClass{  
  8.     static{  
  9.         System.out.println("ConstClass init.");  
  10.     }  
  11.     public final static String value="hello";  
  12. }  
  13.  
  14. public class test{  
  15.     public static void main(String[] args){  
  16.         System.out.println(ConstClass.value);  
  17.     }  

輸出結(jié)果:hello(tip:在編譯的時(shí)候,ConstClass.value已經(jīng)被轉(zhuǎn)變成hello常量放進(jìn)test類的常量池里面了)

以上是針對(duì)類的初始化,接口也要初始化,接口的初始化跟類的初始化有點(diǎn)不同:

上面的代碼都是用static{}來(lái)輸出初始化信息的,接口沒(méi)法做到,但接口初始化的時(shí)候編譯器仍然會(huì)給接口生成一個(gè)<clinit>()的類構(gòu)造器,用來(lái)初始化接口中的成員變量,這點(diǎn)在類的初始化上也有做到。真正不同的地方在于第三點(diǎn),類的初始化執(zhí)行之前要求父類全部都初始化完成了,但接口的初始化貌似對(duì)父接口的初始化不怎么感冒,也就是說(shuō),子接口初始化的時(shí)候并不要求其父接口也完成初始化,只有在真正使用到父接口的時(shí)候它才會(huì)被初始化(比如引用接口上的常量的時(shí)候啦)。

下面分解一下一個(gè)類的加載全過(guò)程:加載->驗(yàn)證->準(zhǔn)備->解析->初始化

首先是加載:

這一塊虛擬機(jī)要完成3件事:

1.通過(guò)一個(gè)類的全限定名來(lái)獲取定義此類的二進(jìn)制字節(jié)流。

2.將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。

3.在java堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這些數(shù)據(jù)的訪問(wèn)入口。

關(guān)于***點(diǎn),很靈活,很多技術(shù)都是在這里切入,因?yàn)樗](méi)有限定二進(jìn)制流從哪里來(lái):

從class文件來(lái)->一般的文件加載

從zip包中來(lái)->加載jar中的類

從網(wǎng)絡(luò)中來(lái)->Applet

..........

相比與加載過(guò)程的其他幾個(gè)階段,加載階段可控性***,因?yàn)轭惖募虞d器可以用系統(tǒng)的,也可以用自己寫的,程序猿可以用自己的方式寫加載器來(lái)控制字節(jié)流的獲取。

獲取二進(jìn)制流獲取完成后會(huì)按照jvm所需的方式保存在方法區(qū)中,同時(shí)會(huì)在java堆中實(shí)例化一個(gè)java.lang.Class對(duì)象與堆中的數(shù)據(jù)關(guān)聯(lián)起來(lái)。

加載完成后就要開(kāi)始對(duì)那些字節(jié)流進(jìn)行檢驗(yàn)了(其實(shí)很多步驟是跟上面交叉進(jìn)行的,比如文件格式驗(yàn)證):

檢驗(yàn)的目的:確保class文件的字節(jié)流信息符合jvm的口味,不會(huì)讓jvm感到不舒服。假如class文件是由純粹的java代碼編譯過(guò)來(lái)的,自然不會(huì)出現(xiàn)類似于數(shù)組越界、跳轉(zhuǎn)到不存在的代碼塊等不健康的問(wèn)題,因?yàn)橐坏┏霈F(xiàn)這種現(xiàn)象,編譯器就會(huì)拒絕編譯了。但是,跟之前說(shuō)的一樣,Class文件流不一定是從java源碼編譯過(guò)來(lái)的,也可能是從網(wǎng)絡(luò)或者其他地方過(guò)來(lái)的,甚至你可以自己用16進(jìn)制寫,假如jvm不對(duì)這些數(shù)據(jù)進(jìn)行校驗(yàn)的話,可能一些有害的字節(jié)流會(huì)讓jvm完全崩潰。

檢驗(yàn)主要經(jīng)歷幾個(gè)步驟:文件格式驗(yàn)證->元數(shù)據(jù)驗(yàn)證->字節(jié)碼驗(yàn)證->符號(hào)引用驗(yàn)證

文件格式驗(yàn)證:驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范并 驗(yàn)證其版本是否能被當(dāng)前的jvm版本所處理。ok沒(méi)問(wèn)題后,字節(jié)流就可以進(jìn)入內(nèi)存的方法區(qū)進(jìn)行保存了。后面的3個(gè)校驗(yàn)都是在方法區(qū)進(jìn)行的。

元數(shù)據(jù)驗(yàn)證:對(duì)字節(jié)碼描述的信息進(jìn)行語(yǔ)義化分析,保證其描述的內(nèi)容符合java語(yǔ)言的語(yǔ)法規(guī)范。

字節(jié)碼檢驗(yàn):最復(fù)雜,對(duì)方法體的內(nèi)容進(jìn)行檢驗(yàn),保證其在運(yùn)行時(shí)不會(huì)作出什么出格的事來(lái)。

符號(hào)引用驗(yàn)證:來(lái)驗(yàn)證一些引用的真實(shí)性與可行性,比如代碼里面引了其他類,這里就要去檢測(cè)一下那些來(lái)究竟是否存在;或者說(shuō)代碼中訪問(wèn)了其他類的一些屬性,這里就對(duì)那些屬性的可以訪問(wèn)行進(jìn)行了檢驗(yàn)。(這一步將為后面的解析工作打下基礎(chǔ))

驗(yàn)證階段很重要,但也不是必要的,假如說(shuō)一些代碼被反復(fù)使用并驗(yàn)證過(guò)可靠性了,實(shí)施階段就可以嘗試用-Xverify:none參數(shù)來(lái)關(guān)閉大部分的類驗(yàn)證措施,以簡(jiǎn)短類加載時(shí)間。

接著就上面步驟完成后,就會(huì)進(jìn)入準(zhǔn)備階段了:

這階段會(huì)為類變量(指那些靜態(tài)變量)分配內(nèi)存并設(shè)置類比那輛初始值的階段,這些內(nèi)存在方法區(qū)中進(jìn)行分配。這里要說(shuō)明一下,這一步只會(huì)給那些靜態(tài)變量設(shè)置一個(gè)初始的值,而那些實(shí)例變量是在實(shí)例化對(duì)象時(shí)進(jìn)行分配的。這里的給類變量設(shè)初始值跟類變量的賦值有點(diǎn)不同,比如下面:

  1. public static int value=123

在這一階段,value的值將會(huì)是0,而不是123,因?yàn)檫@個(gè)時(shí)候還沒(méi)開(kāi)始執(zhí)行任何java代碼,123還是不可見(jiàn)的,而我們所看到的把123賦值給value的putstatic指令是程序被編譯后存在于<clinit>(),所以,給value賦值為123是在初始化的時(shí)候才會(huì)執(zhí)行的。

這里也有個(gè)例外:

  1. public static final int value=123

這里在準(zhǔn)備階段value的值就會(huì)初始化為123了。這個(gè)是說(shuō),在編譯期,javac會(huì)為這個(gè)特殊的value生成一個(gè)ConstantValue屬性,并在準(zhǔn)備階段jm就會(huì)根據(jù)這個(gè)ConstantValue的值來(lái)為value賦值了。

完成上步后,就要進(jìn)行解析了。解析好像是對(duì)類的字段,方法等東西進(jìn)行轉(zhuǎn)換,具體涉及到Class文件的格式內(nèi)容,并沒(méi)深入去了解。

初始化過(guò)程是類加載過(guò)程的***一步:

在前面的類加載過(guò)程中,除了在加載階段用戶可以通過(guò)自定義類加載器參與之外,其他的動(dòng)作完全有jvm主導(dǎo),到了初始化這塊,才開(kāi)始真正執(zhí)行java里面的代碼。

這一步將會(huì)執(zhí)行一些預(yù)操作,注意區(qū)分在準(zhǔn)備階段,已經(jīng)為類變量執(zhí)行過(guò)一次系統(tǒng)賦值了。

其實(shí)說(shuō)白了,這一步就是執(zhí)行程序的<clinit>();方法的過(guò)程。下面我們來(lái)研究一下<clinit>()方法:

<clinit>()方法叫做類構(gòu)造器方法,有編譯器自動(dòng)手機(jī)類中的所有類變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊中的語(yǔ)句合并而成的,置于他們的順序與在源文件中排列的一樣。

<clinit>();方法與類構(gòu)造方法不一樣,他不需要顯示得調(diào)用父類的<clinit>();方法,虛擬機(jī)會(huì)保證子類的<clinit>();方法在執(zhí)行前父類的這個(gè)方法已經(jīng)執(zhí)行完畢了,也就是說(shuō),虛擬機(jī)中***個(gè)被執(zhí)行的<clinit>();方法肯定是java.lang.Object類的。

下面來(lái)個(gè)例子說(shuō)明一下:

  1. static class Parent{  
  2.     public static int A=1;  
  3.     static{  
  4.         A=2;  
  5.     }  
  6. }  
  7. static class Sub extends Parent{  
  8.     public static int B=A;  
  9. }  
  10. public static void main(String[] args){  
  11.     System.out.println(Sub.B);  

首先Sub.B中對(duì)靜態(tài)數(shù)據(jù)進(jìn)行了引用,Sub類要進(jìn)行初始化了。同時(shí),其父類Parent要先進(jìn)行初始化動(dòng)作。Parent初始化后,A=2,所以B=2;上個(gè)過(guò)程相當(dāng)于:

  1. static class Parent{  
  2.     <clinit>(){  
  3.         public static int A=1;  
  4.         static{  
  5.             A=2;  
  6.         }  
  7.     }  
  8. }  
  9. static class Sub extends Parent{  
  10.     <clinit>(){  //jvm會(huì)先讓父類的該方法執(zhí)行完在執(zhí)行這里  
  11.     public static int B=A;  
  12.     }  
  13. }  
  14. public static void main(String[] args){  
  15.     System.out.println(Sub.B);  

<clinit>();方法對(duì)類跟接口來(lái)說(shuō)不是必須的,加入類或者接口中沒(méi)有對(duì)類變量進(jìn)行賦值且沒(méi)有靜態(tài)代碼塊,<clinit>()方法就不會(huì)被編譯器生成。

由于接口里面不能存在static{}這種靜態(tài)代碼塊,但仍然可能存在變量初始化時(shí)的變量賦值操作,所以接口里面也會(huì)生成<clinit>()構(gòu)造器。但跟類的不同的是,執(zhí)行子接口的<clinit>();方法前并不需要執(zhí)行父接口的<clinit>();方法,當(dāng)父接口中定義的變量被使用時(shí),父接口才會(huì)被初始化。

另外,接口的實(shí)現(xiàn)類在初始化的時(shí)候也一樣不會(huì)執(zhí)行接口的<clinit>();方法。

另外,jvm會(huì)保證一個(gè)類的<clinit>();方法在多線程環(huán)境下能被正確地加鎖同步。<因?yàn)槌跏蓟粫?huì)被執(zhí)行一次>。

下面用個(gè)例子說(shuō)明一下:

  1. public class DeadLoopClass {  
  2.  
  3.     static{  
  4.         if(true){  
  5.         System.out.println("要被 ["+Thread.currentThread()+"] 初始化了,下面來(lái)一個(gè)無(wú)限循環(huán)");  
  6.         while(treu){}     
  7.         }  
  8.     }  
  9.       
  10.     /**  
  11.      * @param args  
  12.      */ 
  13.     public static void main(String[] args) {  
  14.         // TODO Auto-generated method stub  
  15.         System.out.println("toplaile");  
  16.         Runnable run=new Runnable(){  
  17.  
  18.             @Override 
  19.             public void run() {  
  20.                 // TODO Auto-generated method stub  
  21.                 System.out.println("["+Thread.currentThread()+"] 要去實(shí)例化那個(gè)類了");  
  22.                 DeadLoopClass d=new DeadLoopClass();  
  23.                 System.out.println("["+Thread.currentThread()+"] 完成了那個(gè)類的初始化工作");  
  24.                   
  25.             }};  
  26.               
  27.             new Thread(run).start();  
  28.             new Thread(run).start();  
  29.     }  
  30.  

這里面,運(yùn)行的時(shí)候?qū)?huì)看到阻塞現(xiàn)象。

呼呼~先到這里。

原文鏈接:http://my.oschina.net/volador/blog/87194

責(zé)任編輯:林師授 來(lái)源: OSCHINA
相關(guān)推薦

2011-02-22 10:46:02

Samba配置

2009-10-23 14:31:05

VB.NET類定義

2009-12-08 17:56:16

WCF配置

2011-04-18 15:56:10

軟件測(cè)試

2011-01-21 17:51:52

2009-04-13 12:37:18

2011-09-06 15:38:20

QT安裝

2010-03-01 17:01:03

Python編程技巧

2009-06-10 16:55:42

cygwin netb安裝

2010-03-10 13:24:45

Zend Debugg

2010-11-19 10:11:49

Oracle物化視圖

2010-06-17 13:10:09

Linux Grub修

2019-05-14 15:27:31

MongoDB自動(dòng)備份數(shù)據(jù)庫(kù)

2010-07-21 14:51:19

telnet-serv

2011-03-11 10:39:02

YUM安裝LAMP

2010-06-12 10:03:20

Ubuntu Grub

2010-03-17 17:20:15

Java class線

2009-12-15 16:51:32

2010-06-11 13:15:07

UML軟件

2010-02-23 11:35:38

Python 項(xiàng)目測(cè)試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)