成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

java類加載機(jī)制

garfileo / 2145人閱讀

摘要:在加載階段,虛擬機(jī)要完成件事情通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。前面的階段中,除了加載的時(shí)候,可以由用戶指定自定義類加載器之外,別的都是由虛擬機(jī)主導(dǎo)控制。

java類加載機(jī)制

代碼編譯的結(jié)果從本地機(jī)器碼轉(zhuǎn)變?yōu)樽止?jié)碼,是存儲(chǔ)格式發(fā)展的一小步,確實(shí)編程語言發(fā)展的一大步

虛擬機(jī)把描述類的數(shù)據(jù)從class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的java類型,這就是虛擬機(jī)的類加載機(jī)制。

1 類的生命周期

一個(gè)類從被加載到內(nèi)存到卸載出內(nèi)存,整個(gè)生命周期包括:

加載loading

驗(yàn)證verification

準(zhǔn)備preparation

解析resolution

初始化initialization

使用using

卸載unloading

其中驗(yàn)證、準(zhǔn)備和解析,這三步合起來又被稱為連接(liking)。

加載、驗(yàn)證、準(zhǔn)備、初始化和卸載,這五個(gè)階段的順序是確定的,而解析不一定。某些情況下,解析可能在初始化之后再開始,這就是java動(dòng)態(tài)綁定。

java虛擬機(jī)規(guī)范中嚴(yán)格規(guī)定了有且只有5種情況必須對(duì)類立即進(jìn)行初始化:

遇到new、getstatic、putstatic或invokestatic這四個(gè)指令時(shí),必須進(jìn)行初始化。

生成這幾個(gè)指令的場(chǎng)景有:

使用new實(shí)例化一個(gè)對(duì)象時(shí);

讀取或者設(shè)置一個(gè)類的靜態(tài)字段時(shí);

調(diào)用一個(gè)類的靜態(tài)方法時(shí)。

使用reflect包的方法對(duì)類進(jìn)行反射時(shí),也觸發(fā)初始化。

初始化一個(gè)類的時(shí)候,若父類還未初始化,則首先進(jìn)行父類的初始化。

包含main方法的那個(gè)類,虛擬機(jī)啟動(dòng)時(shí)會(huì)首先初始化這個(gè)主類。

當(dāng)使用jdk1.7的動(dòng)態(tài)語言支持時(shí),

接口的加載和類加載的過程稍有些不同:

接口和類一樣都有初始化過程,雖然接口里面不能有static{}語句塊,但是編譯器仍然會(huì)為接口生成()類構(gòu)造器,用于初始化接口中所定義的成員變量。

java接口中的變量必須得是final靜態(tài)的,但接口里最好不要有變量。

當(dāng)一個(gè)類初始化時(shí),必須要求父類全部都已經(jīng)初始化,但是接口在初始化時(shí)并不要求其父接口也全部初始化,只有在使用到父接口時(shí)才會(huì)初始化。

2 類加載的過程 2.1 加載

加載是類加載的一個(gè)階段。在加載階段,虛擬機(jī)要完成3件事情:

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

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

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

加載階段完成之后,虛擬機(jī)外部的二進(jìn)制字節(jié)流就按照虛擬機(jī)所需的格式存儲(chǔ)在方法區(qū)之中。

2.2 驗(yàn)證

驗(yàn)證階段是連接階段的第一步,目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。

文件格式驗(yàn)證

驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理。

元數(shù)據(jù)驗(yàn)證

對(duì)字節(jié)碼描述的信息進(jìn)行語義分析,以保證其描述信息符合java語言規(guī)范。

字節(jié)碼驗(yàn)證

最復(fù)雜的一個(gè)階段,通過對(duì)數(shù)據(jù)流和控制流分析,確定程序語義是合法的、符合邏輯的。

符號(hào)引用驗(yàn)證

對(duì)類自身以外(常量池中的各種符號(hào)引用)的信息進(jìn)行匹配性校驗(yàn)。目的是為了確保解析動(dòng)作能正常執(zhí)行。

驗(yàn)證階段是非常重要的,但不是一定必要的階段。如果所運(yùn)行的代碼都已經(jīng)被反復(fù)使用和驗(yàn)證過,就可以通過jvm參數(shù)來關(guān)閉大部分類驗(yàn)證措施。

2.3 準(zhǔn)備

準(zhǔn)備階段是給類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。

此時(shí)進(jìn)行內(nèi)存分配的變量?jī)H包括類變量,而不包含實(shí)例變量,實(shí)例變量將在對(duì)象實(shí)例化時(shí)隨著對(duì)象一起分配在java堆中。

這里所說的初始值是指數(shù)據(jù)類型的零值,比如:

public static int v = 123;

那v的值在準(zhǔn)備階段是0,而不是123。

數(shù)據(jù)類型 零值 數(shù)據(jù)類型 零值
int 0 boolean false
long 0L float 0.0f
short (short)0 double 0.0d
char "u0000" reference null
byte (byte)0

如果一個(gè)變量是常量,或者final類型的,那么在準(zhǔn)備階段就被初始化為常量值,如:

public static final int v = 123;

此時(shí)v的值在準(zhǔn)備階段是123。

2.4 解析

解析階段是虛擬機(jī)將常量池的符號(hào)引用替換成直接引用的過程。

符號(hào)引用:是以一組符號(hào)來描述所引用的目標(biāo),符號(hào)可以是任何形式的字面量,只要使用的時(shí)候能無歧義的定位到目標(biāo)即可。符號(hào)引用與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無關(guān),引用的目標(biāo)并不一定加載到內(nèi)存中。各種虛擬機(jī)的內(nèi)存布局可以各不相同,但是能接受的符號(hào)引用必須是一致的,因?yàn)榉?hào)引用的字面量形式明確定義在java虛擬機(jī)規(guī)范的Class文件格式中。

直接引用:直接引用可以是直接指向目標(biāo)的指針、相對(duì)偏移量或者是一個(gè)能間接定位到目標(biāo)的句柄。直接引用是和虛擬機(jī)實(shí)現(xiàn)的內(nèi)存相關(guān)的,如果有了直接引用,那么引用的目標(biāo)必定已經(jīng)在內(nèi)存中了。

解析主要是針對(duì)類或者接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點(diǎn)限定符7類符號(hào)引用進(jìn)行的。

2.5 初始化

類初始化時(shí)類加載過程的最后一步。前面的階段中,除了加載的時(shí)候,可以由用戶指定自定義類加載器之外,別的都是由虛擬機(jī)主導(dǎo)控制。初始化階段才真正執(zhí)行類中定義的java代碼。

在準(zhǔn)備階段變量已經(jīng)被賦過零值,而初始化階段是根據(jù)程序里面的來初始化類變量和其他資源,可以理解為執(zhí)行類構(gòu)造器的()方法的過程。

()方法是有編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語句塊static{}中的語句合并產(chǎn)生的。編譯器的收集順序是由語句在源文件中出現(xiàn)的順序來決定的,靜態(tài)語句塊中只能訪問到定義在靜態(tài)語句塊之前的變量,定義在它之后的變量,在前面的靜態(tài)語句塊中可以賦值,但是不能訪問。如:

public class Test{
  static{
    i=0;                          //這句話是給變量賦值,可以編譯通過
    System.out.println(i);        //這句話是要訪問i,編譯器會(huì)提示“非法向前引用”編譯不過。   
  }
  static int i = 1;
}

()方法和類的構(gòu)造函數(shù)不同,它不需要顯示調(diào)用父類構(gòu)造器,虛擬機(jī)會(huì)保證在子類的()方法執(zhí)行之前,父類的()方法已經(jīng)執(zhí)行完畢。因此虛擬機(jī)第一個(gè)被執(zhí)行()方法的類肯定是java.lang.Object。

由上一條可以得出結(jié)論,父類中定義的靜態(tài)語句塊要早于子類的變量賦值操作。

()方法對(duì)類或者接口不是必須的,如果一個(gè)類中沒有靜態(tài)語句塊,也沒有對(duì)變量進(jìn)行賦值操作,那么編譯器就不會(huì)為類生成()方法。

前面加載的時(shí)候有說到,接口中不能有靜態(tài)語句塊,但是可以有變量的初始化賦值操作。接口和類都會(huì)生成()方法,到那時(shí)接口執(zhí)行()方法時(shí)不需要先執(zhí)行父接口的()方法,只有當(dāng)父接口定義的變量被使用的時(shí)候,父接口才被初始化。另外,接口的實(shí)現(xiàn)類在初始化的時(shí)候,也不用執(zhí)行接口的()方法。

虛擬機(jī)會(huì)保證一個(gè)類的()方法在多線程的環(huán)境中被正確的加鎖、同步。

3 類加載器

類加載階段的加載階段,即“通過一個(gè)類的全限定名來獲取描述此類的二進(jìn)制字節(jié)流”這個(gè)動(dòng)作放到j(luò)vm外部實(shí)現(xiàn),使得應(yīng)用程序自己可以決定如何獲取所需要的類。實(shí)現(xiàn)這個(gè)動(dòng)作的代碼模塊稱為“類加載器”。

對(duì)于任意一個(gè)類來說,需要加載它的類加載器和其類本身來保證唯一性。如果同一個(gè)Class文件,被不同的類加載器加載了,那么產(chǎn)生的兩個(gè)類是不相同的。

3.1 類加載器的分類

對(duì)于java虛擬機(jī)來說,只有兩種不同的類加載器:

啟動(dòng)類加載器 Bootstrap ClassLoader:C++實(shí)現(xiàn)的,虛擬機(jī)的一部分。

其他類加載器:java語言實(shí)現(xiàn),獨(dú)立于jvm外部。全部繼承抽象類java.lang.ClassLoader。

從java程序員的角度來看,有三種系統(tǒng)提供的類加載器:

啟動(dòng)類加載器 Bootstrap ClassLoader
負(fù)責(zé)將放在JAVA_HOEM/lib目錄里的,或者是被-Xbootclasspath參數(shù)指定的路徑中的,并且可以被虛擬機(jī)識(shí)別的類庫加載到虛擬機(jī)內(nèi)存中。
啟動(dòng)類加載器無法被java程序直接引用。如果是用戶在編寫自定義類加載器的時(shí)候,需要把加載請(qǐng)求委派給啟動(dòng)類加載器,返回null就行了。

擴(kuò)展類加載器 Extension ClassLoader
負(fù)責(zé)加載JAVA_HOEM/lib/ext目錄中的,或者被java.ext.dirs系統(tǒng)變量指定的所有類庫,開發(fā)者可以直接使用擴(kuò)展類加載器。

應(yīng)用程序類加載器 Application ClassLoader
這個(gè)類加載器是ClassLoader中的getSystemClassLoader()方法中的返回值,所以也稱為系統(tǒng)類加載器,負(fù)責(zé)加載用戶類路徑上指定的類庫。
開發(fā)者可以直接使用此類加載器,如果應(yīng)用程序沒有自定義自己的類加載器,一般情況下這個(gè)就是程序的默認(rèn)類加載器。

開發(fā)者可以自己編寫一些自定義類加載器,用來進(jìn)行特定類的加載。他們的關(guān)系是:

雙親委派模型

3.1 雙親委派模型

雙親委派模型要求除了最頂層的啟動(dòng)類加載器外,其余的加載器都得有自己的父類加載器。這里的類加載器的父子關(guān)系不是通過繼承來實(shí)現(xiàn),而是使用組合關(guān)系來復(fù)用復(fù)加載器的代碼。

雙親委派模型的工作過程是:

如果一個(gè)類加載器收到了類加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類加載器去完成,每一個(gè)層次的類加載器都是這樣。因此,所有的類加載請(qǐng)求最終都會(huì)傳送到最頂層的啟動(dòng)類加載器,只有當(dāng)父加載器反饋?zhàn)约簾o法加載這個(gè)加載請(qǐng)求的時(shí)候,子加載器才會(huì)嘗試自己去加載。

使用這個(gè)模型的好處就是java類隨著它的加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系。比如java.lang.Object,無論哪個(gè)類加載器要加載這個(gè)類的時(shí)候,最終都是委派給最頂端的啟動(dòng)類加載器進(jìn)行加載,因此Object類在程序的各個(gè)類加載器環(huán)境中都是同一個(gè)類。如果不使用這個(gè)模型的話,由各個(gè)類加載器自己加載,就會(huì)出現(xiàn)多個(gè)Object類。

雙親委派模型的邏輯實(shí)現(xiàn)代碼很簡(jiǎn)單:

protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException{
  //首先檢查請(qǐng)求的類是否已經(jīng)被加載過
    Class c = findLoadedClass(name);
    if(c==null){
        if(parent!=null){
            c=parent.loadClass(name, false);
        }else{
            c=findBootstrapClassOrNull(name);
        }
        //如果父類加載器無法加載的時(shí)候,就調(diào)用本身的方法去加載
        if(c==null){
            c=findClass(name);
        }
    }
    if(resolve){
        resolveClass(c);
    }
    return c;
}
3.2 破壞雙親委派模型

雙親委派模型并不是一個(gè)強(qiáng)制性的約束模型,在java世界中,大部分加載器都遵循這個(gè)模型,在java歷史上有三種比較大的被破壞情況。

第一次是jdk1.2發(fā)布的時(shí)候。由于雙親委派模型是在1.2才引入的,java.lang.ClassLoader是在1.0的時(shí)候就存在了,面對(duì)在此之前的用戶自定義類加載器的代碼,java設(shè)計(jì)者添加了一個(gè)findClass方法來作為妥協(xié)。

第二次是JNDI服務(wù)。雙親委派模型很好地解決了各個(gè)類加載器的基礎(chǔ)類統(tǒng)一問題,但是當(dāng)基礎(chǔ)類又回來調(diào)用用戶的代碼就沒辦法了。所以引入了線程上下文 類加載器(Thread Context ClassLaoder)。

第三次就是熱更新熱部署的時(shí)候。代表就是OSGi,每一個(gè)程序模塊都有一個(gè)自己的類加載器,當(dāng)需要更換一個(gè)模塊的時(shí)候,就把模塊連同其加載器一起換掉。此時(shí)的類加載器的結(jié)構(gòu)成了網(wǎng)狀結(jié)構(gòu)了。

4 寫在最后

把書從后面往前面看還是挺有意思的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/70758.html

相關(guān)文章

  • Java加載機(jī)制

    摘要:如果需要支持類的動(dòng)態(tài)加載或需要對(duì)編譯后的字節(jié)碼文件進(jìn)行解密操作等,就需要與類加載器打交道了。雙親委派模型,雙親委派模型,約定類加載器的加載機(jī)制。任何之類的字節(jié)碼都無法調(diào)用方法,因?yàn)樵摲椒ㄖ荒茉陬惣虞d的過程中由調(diào)用。 jvm系列 垃圾回收基礎(chǔ) JVM的編譯策略 GC的三大基礎(chǔ)算法 GC的三大高級(jí)算法 GC策略的評(píng)價(jià)指標(biāo) JVM信息查看 GC通用日志解讀 jvm的card table數(shù)據(jù)...

    aervon 評(píng)論0 收藏0
  • jvm加載機(jī)制

    摘要:前面提到,對(duì)于數(shù)組類來說,它并沒有對(duì)應(yīng)的字節(jié)流,而是由虛擬機(jī)直接生成的。對(duì)于其他的類來說,虛擬機(jī)則需要借助類加載器來完成查找字節(jié)流的過程。驗(yàn)證階段的目的,在于確保被加載類能夠滿足虛擬機(jī)的約束條件。 Java 虛擬機(jī)將字節(jié)流轉(zhuǎn)化為 Java 類的過程。這個(gè)過程可分為加載、鏈接以及初始化 三大步驟。 加載是指查找字節(jié)流,并且據(jù)此創(chuàng)建類的過程。加載需要借助類加載器,在 Java 虛擬機(jī)中,類...

    lastSeries 評(píng)論0 收藏0
  • Java加載機(jī)制

    摘要:當(dāng)前類加載器和所有父類加載器都無法加載該類時(shí),拋出異常。加載兩份相同的對(duì)象的情況和不屬于父子類加載器關(guān)系,并且各自都加載了同一個(gè)類。類加載機(jī)制與接口當(dāng)虛擬機(jī)初始化一個(gè)類時(shí),不會(huì)初始化該類實(shí)現(xiàn)的接口。 類加載機(jī)制 概念 類加載器把class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,存放在方法區(qū),然后在堆區(qū)創(chuàng)建一個(gè)java.lang.Class對(duì)象,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)。 1、加載: 查...

    aaron 評(píng)論0 收藏0
  • JAVA加載機(jī)制全解析

    摘要:當(dāng)程序使用某個(gè)類時(shí),如果該類還沒被初始化,加載到內(nèi)存中,則系統(tǒng)會(huì)通過加載連接初始化三個(gè)過程來對(duì)該類進(jìn)行初始化。一旦一個(gè)類被加載到中之后,就不會(huì)再次載入了。它既可以從本地文件系統(tǒng)獲取二進(jìn)制文件來加載類,也可以遠(yuǎn)程主機(jī)獲取二進(jìn)制文件來加載類。 當(dāng)程序使用某個(gè)類時(shí),如果該類還沒被初始化,加載到內(nèi)存中,則系統(tǒng)會(huì)通過加載、連接、初始化三個(gè)過程來對(duì)該類進(jìn)行初始化。該過程就被稱為類的初始化 類加載 ...

    tomener 評(píng)論0 收藏0
  • 從一無所知到無所不知————jvm系列(1)

    摘要:學(xué)習(xí)能更深入的理解這門語言,能理解語言底層的執(zhí)行過程,深入到字節(jié)碼層次。 目錄 ? 前言 程序的運(yùn)行 1.JVM類加載機(jī)制 ①一般在什么情況下會(huì)去加載一個(gè)類?也就是說,什么時(shí)候.class字節(jié)碼文件中加載這個(gè)類到JVM內(nèi)存里來? ②驗(yàn)證、準(zhǔn)備、初始化 ③初始化 2.類加載器和雙親委派機(jī)制 ...

    Betta 評(píng)論0 收藏0
  • Java基礎(chǔ)-加載器以及加載機(jī)制

    摘要:以上文中的類的加載過程為例,它的加載器為系統(tǒng)類加載器。自定義加載器編寫自定義加載器并不困難,只要繼承抽象類并覆蓋方法就行了。源碼來自參考資料類加載機(jī)制與類加載器架構(gòu)深入探討類加載器 序 我是在關(guān)于Java的面試題里了解到類加載器的,在這之前從未想過Java里類是如何被加載、解析的,一直以為只要Import就好了。事實(shí)上Java類加載器是一塊非常重要的內(nèi)容,可以用在類層次劃分、OSGi、...

    stefan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<