Java類加載機制

前言

我們知道我們寫的程序經(jīng)過編譯后成為了.class文件变擒,.class文件中描述了類的各種信息,最終都需要加載到虛擬機之后才能運行和使用肾筐。而虛擬機如何加載這些.class文件哆料?.class文件的信息進入到虛擬機后會發(fā)生什么變化?這些都是本文要講的內容吗铐,文章將會講解加載類加載的每個階段Java虛擬機需要做什么事(加粗標紅)东亦。

什么是類的加載

類的加載指的是將類的.class文件中的二進制數(shù)據(jù)讀入到內存中,將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內唬渗,然后在堆區(qū)創(chuàng)建一個java.lang.Class對象典阵,用來封裝類在方法區(qū)內的數(shù)據(jù)結構。類的加載的最終產品是位于堆區(qū)中的Class對象谣妻,Class對象封裝了類在方法區(qū)內的數(shù)據(jù)結構萄喳,并且向Java程序員提供了訪問方法區(qū)內的數(shù)據(jù)結構的接口。

類加載器并不需要等到某個類被“首次主動使用”時再加載它蹋半,JVM規(guī)范允許類加載器在預料某個類將要被使用時就預先加載它他巨,如果在預先加載的過程中遇到了.class文件缺失或存在錯誤,類加載器必須在程序首次主動使用該類時才報告錯誤(LinkageError錯誤)如果這個類一直沒有被程序主動使用,那么類加載器就不會報告錯誤染突。

一捻爷、類的加載過程

JVM將類的加載分為3個步驟:

1、裝載(Load)

2份企、鏈接(Link)

3也榄、初始化(Initialize)

其中 鏈接(Link)又分3個步驟,如下圖所示:


加載Loading

加載是類加載的第一個階段司志。有兩種時機會觸發(fā)類加載:

1甜紫、預加載。虛擬機啟動時加載骂远,加載的是JAVA_HOME/lib/下的rt.jar下的.class文件囚霸,這個jar包里面的內容是程序運行時非常常常用到的,像java.lang.*激才、java.util.*拓型、java.io.*等等,因此隨著虛擬機一起加載瘸恼。要證明這一點很簡單劣挫,寫一個空的main函數(shù),設置虛擬機參數(shù)為"-XX:+TraceClassLoading"來獲取類加載信息东帅,運行一下:

1 [Opened E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]2 [Loaded java.lang.Object from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]3 [Loaded java.io.Serializable from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]4 [Loaded java.lang.Comparable from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]5 [Loaded java.lang.CharSequence from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]6 [Loaded java.lang.String from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]7 [Loaded java.lang.reflect.GenericDeclaration from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]8 [Loaded java.lang.reflect.Type from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]9 [Loaded java.lang.reflect.AnnotatedElement from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]10 [Loaded java.lang.Class from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]11 [Loaded java.lang.Cloneable from E:\MyEclipse10\Common\binary\com.sun.java.jdk.win32.x86_64_1.6.0.013\jre\lib\rt.jar]12 ...

2压固、運行時加載。虛擬機在用到一個.class文件的時候冰啃,會先去內存中查看一下這個.class文件有沒有被加載邓夕,如果沒有就會按照類的全限定名來加載這個類。

那么阎毅,加載階段做了什么焚刚,其實加載階段做了有三件事情:

1、獲取.class文件的二進制流

2扇调、將類信息矿咕、靜態(tài)變量、字節(jié)碼狼钮、常量這些.class文件中的內容放入方法區(qū)中

3碳柱、在內存中生成一個代表這個.class文件的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口熬芜。一般這個Class是在堆里的莲镣,不過HotSpot虛擬機比較特殊,這個Class對象是放在方法區(qū)中的

虛擬機規(guī)范對這三點的要求并不具體涎拉,因此虛擬機實現(xiàn)與具體應用的靈活度都是相當大的瑞侮。例如第一條的圆,根本沒有指明二進制字節(jié)流要從哪里來、怎么來半火,因此單單就這一條越妈,就能變出許多花樣來:

· 從zip包中獲取,這就是以后jar钮糖、ear梅掠、war格式的基礎

· 從網(wǎng)絡中獲取,典型應用就是Applet

· 運行時計算生成店归,典型應用就是動態(tài)代理技術

· 由其他文件生成阎抒,典型應用就是JSP,即由JSP生成對應的.class文件

· 從數(shù)據(jù)庫中讀取消痛,這種場景比較少見

總而言之挠蛉,在類加載整個過程中,這部分是對于開發(fā)者來說可控性最強的一個階段肄满。

驗證

連接階段的第一步,這一階段的目的是為了確保.class文件的字節(jié)流中包含的信息符合當前虛擬機的要求质涛,并且不會危害虛擬機自身的安全稠歉。

Java語言本身是相對安全的語言(相對C/C++來說),但是前面說過汇陆,.class文件未必要從Java源碼編譯而來怒炸,可以使用任何途徑產生,甚至包括用十六進制編輯器直接編寫來產生.class文件毡代。在字節(jié)碼語言層面上阅羹,Java代碼至少從語義上是可以表達出來的。虛擬機如果不檢查輸入的字節(jié)流教寂,對其完全信任的話捏鱼,很可能會因為載入了有害的字節(jié)流而導致系統(tǒng)崩潰,所以驗證是虛擬機對自身保護的一項重要工作酪耕。

驗證階段將做一下幾個工作导梆,具體就不細講了,這是虛擬機實現(xiàn)層面的問題:

1迂烁、文件格式驗證

這個地方要說一點和開發(fā)者相關的看尼。.class文件的第5~第8個字節(jié)表示的是該.class文件的主次版本號,驗證的時候會對這4個字節(jié)做一個驗證盟步,高版本的JDK能向下兼容以前版本的.class文件藏斩,但不能運行以后的class文件,即使文件格式未發(fā)生任何變化却盘,虛擬機也必須拒絕執(zhí)行超過其版本號的.class文件狰域。舉個具體的例子媳拴,如果一段.java代碼是在JDK1.6下編譯的,那么JDK1.6北专、JDK1.7的環(huán)境能運行這個.java代碼生成的.class文件禀挫,但是JDK1.5、JDK1.4乃更低的JDK版本是無法運行這個.java代碼生成的.class文件的拓颓。如果運行语婴,會拋出java.lang.UnsupportedClassVersionError,這個小細節(jié)驶睦,務必注意砰左。

2、元數(shù)據(jù)驗證

3场航、字節(jié)碼驗證

4缠导、符號引用驗證

1) 裝載:查找并加載類的二進制數(shù)據(jù)(查找和導入Class文件)

加載是類加載過程的第一個階段,在加載階段溉痢,虛擬機需要完成以下三件事情:

1僻造、通過一個類的全限定名來獲取其定義的二進制字節(jié)流。

2孩饼、將這個字節(jié)流所代表的靜態(tài)存儲結構轉化為方法區(qū)的運行時數(shù)據(jù)結構髓削。

3、在Java堆中生成一個代表這個類的java.lang.Class對象镀娶,作為對方法區(qū)中這些數(shù)據(jù)的訪問入口立膛。

相對于類加載的其他階段而言,加載階段(準確地說梯码,是加載階段獲取類的二進制字節(jié)流的動作)是可控性最強的階段宝泵,因為開發(fā)人員既可以使用系統(tǒng)提供的類加載器來完成加載,也可以自定義自己的類加載器來完成加載轩娶。

加載階段完成后儿奶,虛擬機外部的 二進制字節(jié)流就按照虛擬機所需的格式存儲在方法區(qū)之中,而且在Java堆中也創(chuàng)建一個java.lang.Class類的對象罢坝,這樣便可以通過該對象訪問方法區(qū)中的這些數(shù)據(jù)廓握。

2) 鏈接(分3個步驟

1、驗證:確保被加載的類的正確性

驗證是連接階段的第一步嘁酿,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當前虛擬機的要求隙券,并且不會危害虛擬機自身的安全。驗證階段大致會完成4個階段的檢驗動作:

文件格式驗證:驗證字節(jié)流是否符合Class文件格式的規(guī)范闹司;例如:是否以0xCAFEBABE開頭娱仔、主次版本號是否在當前虛擬機的處理范圍之內、常量池中的常量是否有不被支持的類型游桩。

元數(shù)據(jù)驗證:對字節(jié)碼描述的信息進行語義分析(注意:對比javac編譯階段的語義分析)牲迫,以保證其描述的信息符合Java語言規(guī)范的要求耐朴;例如:這個類是否有父類,除了java.lang.Object之外盹憎。

字節(jié)碼驗證:通過數(shù)據(jù)流和控制流分析筛峭,確定程序語義是合法的、符合邏輯的陪每。

符號引用驗證:確保解析動作能正確執(zhí)行影晓。

驗證階段是非常重要的,但不是必須的檩禾,它對程序運行期沒有影響挂签,如果所引用的類經(jīng)過反復驗證,那么可以考慮采用-Xverifynone參數(shù)來關閉大部分的類驗證措施盼产,以縮短虛擬機類加載的時間饵婆。

2、準備:為類的靜態(tài)變量分配內存戏售,并將其初始化為默認值

準備階段是正式為類變量分配內存并設置類變量初始值的階段侨核,這些內存都將在方法區(qū)中分配。對于該階段有以下幾點需要注意:

1灌灾、這時候進行內存分配的僅包括類變量(static)芹关,而不包括實例變量,實例變量會在對象實例化時隨著對象一塊分配在Java堆中紧卒。

2、這里所設置的初始值通常情況下是數(shù)據(jù)類型默認的零值(如0诗祸、0L跑芳、null、false等)直颅,而不是被在Java代碼中被顯式地賦予的值博个。

假設一個類變量的定義為:public static int value = 3; 那么變量value在準備階段過后的初始值為0功偿,而不是3盆佣,因為這時候尚未開始執(zhí)行任何Java方法,而把value賦值為3的putstatic指令是在程序編譯后械荷,存放于類構造器()方法之中的共耍,所以把value賦值為3的動作將在初始化階段才會執(zhí)行。

3吨瞎、解析:把類中的符號引用轉換為直接引用

解析階段是虛擬機將常量池內的符號引用替換為直接引用的過程痹兜,解析動作主要針對類或接口、字段颤诀、類方法字旭、接口方法对湃、方法類型、方法句柄和調用限定符7類符號引用進行遗淳。符號引用就是一組符號來描述目標拍柒,可以是任何字面量。

直接引用就是直接指向目標的指針屈暗、相對偏移量或一個間接定位到目標的句柄拆讯。

3) 初始化:對類的靜態(tài)變量,靜態(tài)代碼塊執(zhí)行初始化操作

初始化恐锦,為類的靜態(tài)變量賦予正確的初始值往果,JVM負責對類進行初始化,主要對類變量進行初始化一铅。在Java中對類變量進行初始值設定有兩種方式:

①聲明類變量是指定初始值陕贮。

②使用靜態(tài)代碼塊為類變量指定初始值。

類的初始化

類什么時候才被初始化:

1)創(chuàng)建類的實例潘飘,也就是new一個對象

2)訪問某個類或接口的靜態(tài)變量肮之,或者對該靜態(tài)變量賦值

3)調用類的靜態(tài)方法

4)反射(Class.forName("com.lyj.load"))

5)初始化一個類的子類(會首先初始化子類的父類)

6)JVM啟動時標明的啟動類,即文件名和類名相同的那個類 只有這6中情況才會導致類的類的初始化卜录。

類的初始化步驟 / JVM初始化步驟:

1)如果這個類還沒有被加載和鏈接戈擒,那先進行加載和鏈接

2)假如這個類存在直接父類,并且這個類還沒有被初始化(注意:在一個類加載器中艰毒,類只能初始化一次)筐高,那就初始化直接的父類(不適用于接口)

3 ) 假如類中存在初始化語句(如static變量和static塊),那就依次執(zhí)行這些初始化語句丑瞧。

類的加載

類的加載指的是將類的.class文件中的二進制數(shù)據(jù)讀入到內存中柑土,將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內,然后在堆區(qū)創(chuàng)建一個這個類的Java.lang.Class對象绊汹,用來封裝類在方法區(qū)類的對象稽屏。

類的加載的最終產品是位于堆區(qū)中的Class對象。 Class對象封裝了類在方法區(qū)內的數(shù)據(jù)結構西乖,并且向Java程序員提供了訪問方法區(qū)內的數(shù)據(jù)結構的接口狐榔。

加載類的方式有以下幾種:

1)從本地系統(tǒng)直接加載

2)通過網(wǎng)絡下載.class文件

3)從zip,jar等歸檔文件中加載.class文件

4)從專有數(shù)據(jù)庫中提取.class文件

5)將Java源文件動態(tài)編譯為.class文件(服務器)

6)命令行啟動應用時候由JVM初始化加載

7)通過Class.forName()方法動態(tài)加載

8)通過ClassLoader.loadClass()方法動態(tài)加載?

加載器

JVM的類加載是通過ClassLoader及其子類來完成的获雕,類的層次關系和加載順序可以由下圖來描述:

1)Bootstrap ClassLoader?負責加載$JAVA_HOME中 jre/lib/rt.jar 里所有的class或Xbootclassoath選項指定的jar包薄腻。由C++實現(xiàn),不是ClassLoader子類届案。

2)Extension ClassLoader?負責加載java平臺中擴展功能的一些jar包被廓,包括$JAVA_HOME中jre/lib/*.jar 或 -Djava.ext.dirs指定目錄下的jar包。

3)App ClassLoader?負責加載classpath中指定的jar包及 Djava.class.path 所指定目錄下的類和jar包萝玷。

4)Custom ClassLoader?通過java.lang.ClassLoader的子類自定義加載class嫁乘,屬于應用程序根據(jù)自身需要自定義的ClassLoader昆婿,如tomcat、jboss都會根據(jù)j2ee規(guī)范自行實現(xiàn)ClassLoader蜓斧。

加載過程中會先檢查類是否被已加載仓蛆,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查挎春,只要某個classloader已加載看疙,就視為已加載此類,保證此類只所有ClassLoader加載一次直奋。而加載的順序是自頂向下能庆,也就是由上層來逐層嘗試加載此類。

結束生命周期

在如下幾種情況下脚线,Java虛擬機將結束生命周期

1搁胆、執(zhí)行了System.exit()方法

2、程序正常執(zhí)行結束

3邮绿、程序在執(zhí)行過程中遇到了異城裕或錯誤而異常終止

4、由于操作系統(tǒng)出現(xiàn)錯誤而導致Java虛擬機進程終止?


類加載器的分類:

A.從Java虛擬機的角度:

1.Bootstrap ClassLoader啟動類加載器

2.其他類加載器

從JVM的角度船逮,加載器只分為兩類,即JVM自身實現(xiàn)的Bootstrap啟動類加載器顾腊,和其他JVM以外的所有類加載器。Bootstrap翻譯為根挖胃,故也叫根類加載器杂靶。

B.從開發(fā)者的角度:

1.Bootstrap ClassLoader根類加載器

2.Extension ClassLoader拓展類加載器

3.Application ClassLoader應用程序類加載器

1.根類加載器,加載位于/jre/lib目錄中的或者被參數(shù)-Xbootclasspath所指定的目錄下的核心Java類庫酱鸭。此類加載器是Java虛擬機的一部分伪煤,使用native代碼(C++)編寫。

如圖所示凛辣,rt.jar這個jar包就是Bootstrap根類加載器負責加載的,其中包含了java各種核心的類如java.lang,java.io,java.util职烧,java.sql等

2.擴展類加載器扁誓,加載位于/jre/lib/ext目錄中的或者java.ext.dirs系統(tǒng)變量所指定的目錄下的拓展類庫。此加載器由sun.misc.Launcher$ExtClassLoader實現(xiàn)蚀之。

3.系統(tǒng)類加載器蝗敢,加載用戶路徑(ClassPath)上所指定的類庫。此加載器由sun.misc.Launcher$AppClassLoader實現(xiàn)足删。

雙親委派機制

類加載器之間的關系:

應用程序都是由這3種類加載器互相配合進行加載的寿谴,如果有必要還可以加入自己定義的類加載器。這些類加載器之間的關系如下圖:

圖中的層次關系失受,稱為類加載器的雙親委派模型讶泰。雙親委派模型要求除了頂層的根類加載器以外咏瑟,其余的類加載器都應該有自己的父類加載器(一般不是以繼承實現(xiàn),而是使用組合關系來復用父加載器的代碼)痪署。

如果一個類收到類加載請求码泞,它首先請求父類加載器去加載這個類只有當父類加載器無法完成加載時(其目錄搜索范圍內沒找到需要的類)狼犯,子類加載器才會自己去加載余寥。

雙親委派的優(yōu)勢:

使用雙親委派模型來組織類加載器之間的關系,有一個顯而易見的好處就是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關系悯森。例如類java.lang.Object(存放于rt.jar中)宋舷,是所有類的父類,所以任意一個類啟動類加載時瓢姻,都需要先加載Object類祝蝠。在類加載器來看,所有的加載Object類的請求汹来,都會逐級委托续膳,最后都委托給Bootstrap根類加載器加載,因此Object類在程序的各種類加載器環(huán)境中都是同一個類收班。(否則坟岔,系統(tǒng)中出現(xiàn)的Object類都不盡相同則會出現(xiàn)一片混亂)



Java類加載機制

Java虛擬機——類加載機制和類加載器

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市摔桦,隨后出現(xiàn)的幾起案子社付,更是在濱河造成了極大的恐慌偏塞,老刑警劉巖月弛,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異招狸,居然都是意外死亡兄世,警方通過查閱死者的電腦和手機啼辣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來御滩,“玉大人鸥拧,你說我怎么就攤上這事∠鹘猓” “怎么了富弦?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長氛驮。 經(jīng)常有香客問我腕柜,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任盏缤,我火速辦了婚禮砰蠢,結果婚禮上,老公的妹妹穿的比我還像新娘蛾找。我一直安慰自己娩脾,他們只是感情好,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布打毛。 她就那樣靜靜地躺著柿赊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪幻枉。 梳的紋絲不亂的頭發(fā)上碰声,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音熬甫,去河邊找鬼胰挑。 笑死,一個胖子當著我的面吹牛椿肩,可吹牛的內容都是我干的瞻颂。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼郑象,長吁一口氣:“原來是場噩夢啊……” “哼贡这!你這毒婦竟也來了?” 一聲冷哼從身側響起厂榛,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤盖矫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后击奶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辈双,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年柜砾,在試婚紗的時候發(fā)現(xiàn)自己被綠了湃望。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡痰驱,死狀恐怖证芭,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情萄唇,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布术幔,位于F島的核電站另萤,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜四敞,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一泛源、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧忿危,春花似錦达箍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至解滓,卻和暖如春赃磨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洼裤。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工邻辉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腮鞍。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓值骇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親移国。 傳聞我的和親對象是個殘疾皇子吱瘩,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內容