JVM(二)內(nèi)存與垃圾回收|類(lèi)加載子系統(tǒng)

在JVM概述中我們已經(jīng)了解了JVM體系結(jié)構(gòu),下文將深入介紹JVM體系結(jié)構(gòu)之一的類(lèi)加載子系統(tǒng)债鸡。

目錄
?1 類(lèi)加載子系統(tǒng)作用
?2 類(lèi)加載器 ClassLoader角色
?3 類(lèi)加載過(guò)程
?4 類(lèi)加載器分類(lèi)
??4.1 ClassLoader的常用方法及獲取方法
??4.2虛擬機(jī)自帶的加載器
??4.3用戶(hù)自定義類(lèi)加載器
?5 雙親委派機(jī)制
??5.1 工作原理
??5.2 沙箱安全機(jī)制
??5.3 雙親委派機(jī)制的優(yōu)勢(shì)
?6 類(lèi)的主動(dòng)使用和被動(dòng)使用

1 類(lèi)加載子系統(tǒng)作用
類(lèi)加載子系統(tǒng)
  • 類(lèi)加載子系統(tǒng)負(fù)責(zé)從文件系統(tǒng)或者網(wǎng)絡(luò)中加載Class文件江滨,class文件在文件開(kāi)頭有特定的文件標(biāo)識(shí);
  • ClassLoader只負(fù)責(zé)class文件的加載厌均,至于它是否可以運(yùn)行唬滑,則由Execution Engine決定
  • 加載的類(lèi)信息存放于一塊成為方法區(qū)的內(nèi)存空間。除了類(lèi)信息之外棺弊,方法區(qū)還會(huì)存放運(yùn)行時(shí)常量池信息晶密,可能還包括字符串字面量和數(shù)字常量(這部分常量信息是Class文件中常量池部分的內(nèi)存映射)
2 類(lèi)加載器 ClassLoader角色
類(lèi)加載器 ClassLoader角色
  • class file存在本地磁盤(pán)上,可以理解為設(shè)計(jì)師畫(huà)在紙上的模板模她,而最終這個(gè)模板在執(zhí)行時(shí)是要加載到JVM中來(lái)稻艰,根據(jù)這個(gè)文件實(shí)例出n個(gè)一模一樣的實(shí)例。
  • class file加載到JVM中侈净,被稱(chēng)為DNA元數(shù)據(jù)模板尊勿,放在方法區(qū)。
  • 在.class文件-->JVM-->最終成為元數(shù)據(jù)模板畜侦,此過(guò)程就要一個(gè)運(yùn)輸工具(ClassLoader)元扔,扮演快遞員的角色。
3 類(lèi)加載過(guò)程

類(lèi)加載過(guò)程

①加載

  • 通過(guò)一個(gè)類(lèi)的全限定名獲取定義此類(lèi)的二進(jìn)制字節(jié)流旋膳;
  • 將這個(gè)字節(jié)流所代表的的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)澎语;
  • 在內(nèi)存中生成一個(gè)代表這個(gè)類(lèi)的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類(lèi)的各種數(shù)據(jù)的訪(fǎng)問(wèn)入口。

②驗(yàn)證

  • 目的在于確保Class文件的字節(jié)流中包含信息符合當(dāng)前虛擬機(jī)要求咏连,保證被加載類(lèi)的正確性盯孙,不會(huì)危害虛擬機(jī)自身安全鲁森。
  • 主要包括四種驗(yàn)證祟滴,文件格式驗(yàn)證(java的class文件都是以CAFEBABE開(kāi)頭),源數(shù)據(jù)驗(yàn)證歌溉,字節(jié)碼驗(yàn)證垄懂,符號(hào)引用驗(yàn)證。

③準(zhǔn)備

  • 為類(lèi)變量分配內(nèi)存并且設(shè)置該類(lèi)變量的默認(rèn)初始值痛垛,即零值草慧;例如:


  • 這里不包含用final修飾的static,因?yàn)閒inal在編譯的時(shí)候就會(huì)分配了匙头,準(zhǔn)備階段會(huì)顯式初始化漫谷;

  • 不會(huì)為實(shí)例變量分配初始化,類(lèi)變量會(huì)分配在方法去中蹂析,而實(shí)例變量是會(huì)隨著對(duì)象一起分配到j(luò)ava堆中舔示。

④解析

  • 將常量池內(nèi)的符號(hào)引用轉(zhuǎn)換為直接引用的過(guò)程。
  • 事實(shí)上电抚,解析操作網(wǎng)晚會(huì)伴隨著jvm在執(zhí)行完初始化之后再執(zhí)行
  • 符號(hào)引用就是一組符號(hào)來(lái)描述所引用的目標(biāo)惕稻。符號(hào)應(yīng)用的字面量形式明確定義在《java虛擬機(jī)規(guī)范》的class文件格式中。直接引用就是直接指向目標(biāo)的指針蝙叛、相對(duì)偏移量或一個(gè)間接定位到目標(biāo)的句柄
  • 解析動(dòng)作主要針對(duì)類(lèi)或接口俺祠、字段、類(lèi)方法借帘、接口方法蜘渣、方法類(lèi)型等。對(duì)應(yīng)常量池中的CONSTANT_Class_info/CONSTANT_Fieldref_info肺然、CONSTANT_Methodref_info等蔫缸。

⑤初始化

  • 初始化階段就是執(zhí)行類(lèi)構(gòu)造器方法clinit的過(guò)程。


  • clinit方法不需要定義狰挡,是javac編譯器自動(dòng)收集類(lèi)中的所有類(lèi)變量的賦值動(dòng)作和靜態(tài)代碼塊中的語(yǔ)句合并而來(lái)捂龄。


    注:查看字節(jié)碼工具-idea插件jclasslib
  • 構(gòu)造器方法中指令按語(yǔ)句在源文件中出現(xiàn)的順序執(zhí)行

  • clinit()不同于類(lèi)的構(gòu)造器。(關(guān)聯(lián):構(gòu)造器是虛擬機(jī)視角下的init())

  • 若該類(lèi)具有父類(lèi)加叁,jvm會(huì)保證子類(lèi)的clinit()執(zhí)行前倦沧,父類(lèi)的clinit()已經(jīng)執(zhí)行完畢


  • 虛擬機(jī)必須保證一個(gè)類(lèi)的clinit()方法在多線(xiàn)程下被同步加鎖,保證類(lèi)只加載一次。

4 類(lèi)加載器分類(lèi)
  • JVM支持兩種類(lèi)型的加載器它匕,分別為引導(dǎo)類(lèi)加載器(BootStrap ClassLoader)和自定義類(lèi)加載器(User-Defined ClassLoader)
  • java虛擬機(jī)規(guī)范義展融,將所有派生于抽象類(lèi)ClassLoader的類(lèi)加載器都劃分為自定義類(lèi)加載器。
4.1 ClassLoader的常用方法及獲取方法

在了解JVM分類(lèi)之前豫柬,先來(lái)了解ClassLoader的常用方法及獲取方法

  • ClassLoader類(lèi)告希,它是一個(gè)抽象類(lèi)扑浸,其后所有的類(lèi)加載器都繼承自ClassLoader(不包括啟動(dòng)類(lèi)加載器)
方法名稱(chēng) 描述
getParent() 返回該類(lèi)加載器的超類(lèi)加載器
loadClass(String name) 加載名稱(chēng)為name的類(lèi),返回結(jié)果為java.lang.Class類(lèi)的實(shí)例
findClass(String name) 查找名稱(chēng)為name的類(lèi)燕偶,返回結(jié)果為java.lang.Class類(lèi)的實(shí)例
findLoadedClass(String name) 查找名稱(chēng)為name的已經(jīng)被加載過(guò)的類(lèi)喝噪,返回結(jié)果為java.lang.Class類(lèi)的實(shí)例
defineClass(String name,byte[] b,int off,int len) 把字節(jié)數(shù)組b中的內(nèi)容轉(zhuǎn)換為一個(gè)Java類(lèi) 指么,返回結(jié)果為java.lang.Class類(lèi)的實(shí)例
resolveClass(Class<?> c) 連接指定的一個(gè)java類(lèi)
  • 獲取ClassLoader的途徑

①獲取當(dāng)前類(lèi)的ClassLoader

ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
System.out.println(classLoader); //null

②獲取當(dāng)前線(xiàn)程上下文的ClassLoader

ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();
System.out.println(classLoader1);

③獲取系統(tǒng)的ClassLoader

ClassLoader classLoader2 = ClassLoader.getSystemClassLoader().getParent();
System.out.println(classLoader2);

④獲取調(diào)用者的ClassLoader

  • 拓展類(lèi)加載器和系統(tǒng)類(lèi)加載器間接繼承于ClassLoader抽象類(lèi)


4.2虛擬機(jī)自帶的加載器

①啟動(dòng)類(lèi)加載器(引導(dǎo)類(lèi)加載器酝惧,BootStrap ClassLoader)

  • 這個(gè)類(lèi)加載使用C/C++語(yǔ)言實(shí)現(xiàn)的,嵌套在JVM內(nèi)部


  • 它用來(lái)加載java的核心庫(kù)(JAVA_HOME/jre/lib/rt.jar/resources.jar或sun.boot.class.path路徑下的內(nèi)容)伯诬,用于提供JVM自身需要的類(lèi)


  • 不繼承自java.lang.ClassLoader,沒(méi)有父加載器

  • 加載拓展類(lèi)和應(yīng)用程序類(lèi)加載器晚唇,并指定為他們的父加載器

  • 處于安全考慮,BootStrap啟動(dòng)類(lèi)加載器只加載包名為java盗似、javax哩陕、sun等開(kāi)頭的類(lèi)

②拓展類(lèi)加載器(Extension ClassLoader)

  • java語(yǔ)言編寫(xiě) ,由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn)赫舒。

  • 派生于ClassLoader類(lèi)

  • 父類(lèi)加載器為啟動(dòng)類(lèi)加載器


  • 從java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類(lèi)庫(kù)悍及,或從JDK的安裝目錄的jre/lib/ext子目錄(擴(kuò)展目錄)下加載類(lèi)庫(kù)。如果用戶(hù)創(chuàng)建的JAR放在此目錄下号阿,也會(huì)由拓展類(lèi)加載器自動(dòng)加載


③應(yīng)用程序類(lèi)加載器(系統(tǒng)類(lèi)加載器并鸵,AppClassLoader)

  • java語(yǔ)言編寫(xiě), 由sun.misc.Launcher$AppClassLoader實(shí)現(xiàn)扔涧。

  • 派生于ClassLoader類(lèi)

  • 父類(lèi)加載器為拓展類(lèi)加載器


  • 它負(fù)責(zé)加載環(huán)境變量classpath或系統(tǒng)屬性 java.class.path指定路徑下的類(lèi)庫(kù)

  • 該類(lèi)加載器是程序中默認(rèn)的類(lèi)加載器园担,一般來(lái)說(shuō),java應(yīng)用的類(lèi)都是由它來(lái)完成加載

  • 通過(guò)ClassLoader#getSystemClassLoader()方法可以獲取到該類(lèi)加載器

上文中提及的父子加載器并非簡(jiǎn)單的繼承關(guān)系而是雙親委派模式枯夜,下文第五節(jié)將詳細(xì)說(shuō)明此內(nèi)容弯汰。

4.3用戶(hù)自定義類(lèi)加載器

①為什么要自定義類(lèi)加載器
隔離加載類(lèi)
修改類(lèi)加載的方式
拓展加載源
防止源碼泄漏
②自定義類(lèi)加載器的實(shí)現(xiàn)
參考https://segmentfault.com/a/1190000012925715

5 雙親委派機(jī)制

Java虛擬機(jī)對(duì)class文件采用的是按需加載的方式,也就是說(shuō)當(dāng)需要使用該類(lèi)時(shí)才會(huì)將它的class文件加載到內(nèi)存生成的class對(duì)象湖雹。而且加載某個(gè)類(lèi)的class文件時(shí)咏闪,java虛擬機(jī)采用的是雙親委派模式,即把請(qǐng)求交由父類(lèi)處理摔吏,它是一種任務(wù)委派模式鸽嫂。

5.1 工作原理
  • 如果一個(gè)類(lèi)加載器收到了類(lèi)加載請(qǐng)求,它并不會(huì)自己先去加載征讲,而是把這個(gè)請(qǐng)求委托給父類(lèi)的加載器去執(zhí)行据某。
  • 如果父類(lèi)加載器還存在其父類(lèi)加載器,則進(jìn)一步向上委托诗箍,依次遞歸癣籽,請(qǐng)求最終到達(dá)頂層的啟動(dòng)類(lèi)加載器。
  • 如果父類(lèi)加載器可以完成類(lèi)加載器任務(wù)就成功返回,倘若父類(lèi)加載器無(wú)法完成此加載任務(wù)筷狼,子加載器才會(huì)嘗試自己去加載瓶籽,這就是雙親委派模式。


    雙親委派機(jī)制流程

思考:能不能自己寫(xiě)個(gè)java.lang.String類(lèi)埂材?
如下塑顺,我們自己定義一個(gè)java.lang.String類(lèi)。由于雙親委派機(jī)制楞遏,啟動(dòng)加載器會(huì)加載java核心類(lèi)庫(kù)的String類(lèi)(BootStrap啟動(dòng)類(lèi)加載器加載包名以java茬暇、javax、sun等開(kāi)頭的類(lèi))寡喝,而自己寫(xiě)的String類(lèi)根本沒(méi)有機(jī)會(huì)得到加載。

package java.lang;
public class String {
    //
    static{
        System.out.println("我是自定義的String類(lèi)的靜態(tài)代碼塊");
    }
    public static void main(String[] args) {
        System.out.println("hello,String");
    }
}

什么地方違反了雙親委派模型

5.2 沙箱安全機(jī)制

自定義String類(lèi)勒奇,在加載自定義String類(lèi)的時(shí)會(huì)率先使用引導(dǎo)類(lèi)加載器加載预鬓,而引導(dǎo)類(lèi)加載器在加載過(guò)程中會(huì)先加載jdk自帶的文件(rt.jar包中的java\lang\String.class),報(bào)錯(cuò)信息說(shuō)沒(méi)有main方法就是因?yàn)榧虞d的是rt.jar包中的String類(lèi)。這樣可以保證對(duì)java核心源代碼的保護(hù)赊颠,這就是沙箱安全機(jī)制

在jvm中表示兩個(gè)class對(duì)象是否為同一個(gè)類(lèi)存在的兩個(gè)必要條件:

  • 類(lèi)的完整類(lèi)名必須一致格二,包括包名
  • 加載這個(gè)類(lèi)的ClassLoader(指ClassLoader實(shí)例對(duì)象)必須相同
    換句話(huà)說(shuō),在jvm中竣蹦,即使這兩個(gè)類(lèi)對(duì)象(class對(duì)象)來(lái)源同一個(gè)Class文件顶猜,被同一個(gè)虛擬機(jī)所加載,但只要加載它們的ClassLoader實(shí)例對(duì)象不同痘括,那么這兩個(gè)類(lèi)對(duì)象也是不相等的.
5.3 雙親委派機(jī)制的優(yōu)勢(shì)
  • 避免類(lèi)的重復(fù)加載
  • 保護(hù)程序安全长窄,防止核心API被隨意篡改


6 類(lèi)的主動(dòng)使用和被動(dòng)使用

①JVM必須知道一個(gè)類(lèi)型是有啟動(dòng)類(lèi)加載器加載的還是由用戶(hù)類(lèi)加載器加載的。如果一個(gè)類(lèi)型由用戶(hù)類(lèi)加載器加載的纲菌,那么jvm會(huì)將這個(gè)類(lèi)加載器的一個(gè)引用作為類(lèi)型信息的會(huì)議部分保存在方法區(qū)中挠日。當(dāng)解析一個(gè)類(lèi)型到另一個(gè)類(lèi)型的引用的時(shí)候,JVM需要保證兩個(gè)類(lèi)型的加載器是相同的翰舌。

②java程序?qū)︻?lèi)的使用方式分為:主動(dòng)使用和被動(dòng)使用

主動(dòng)使用嚣潜,分為七種情況:

  • 創(chuàng)建類(lèi)的實(shí)例
  • 訪(fǎng)問(wèn)某各類(lèi)或接口的靜態(tài)變量,或者對(duì)靜態(tài)變量賦值
  • 調(diào)用類(lèi)的靜態(tài)方法
  • 反射椅贱,比如Class.forName(com.test.jvm.xxx)
  • 初始化一個(gè)類(lèi)的子類(lèi)
  • java虛擬機(jī)啟動(dòng)時(shí)被標(biāo)明為啟動(dòng)類(lèi)的類(lèi)
  • JDK 7 開(kāi)始提供的動(dòng)態(tài)語(yǔ)言支持:
  • java.lang.invoke.MethodHandle實(shí)例的解析結(jié)果REF_getStatic懂算、REF_putStatic、REF_invokeStatic句柄對(duì)應(yīng)的類(lèi)沒(méi)有初始化庇麦,則初始化计技。

除了以上七種情況,其他使用java類(lèi)的方式都被看作是對(duì)類(lèi)的被動(dòng)使用女器,都不會(huì)導(dǎo)致類(lèi)的初始化酸役。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涣澡,更是在濱河造成了極大的恐慌贱呐,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件入桂,死亡現(xiàn)場(chǎng)離奇詭異奄薇,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)抗愁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)馁蒂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蜘腌,你說(shuō)我怎么就攤上這事沫屡。” “怎么了撮珠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵沮脖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我芯急,道長(zhǎng)勺届,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任娶耍,我火速辦了婚禮免姿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘榕酒。我一直安慰自己胚膊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布奈应。 她就那樣靜靜地躺著澜掩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪杖挣。 梳的紋絲不亂的頭發(fā)上肩榕,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音惩妇,去河邊找鬼株汉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛歌殃,可吹牛的內(nèi)容都是我干的乔妈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼氓皱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼路召!你這毒婦竟也來(lái)了勃刨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤股淡,失蹤者是張志新(化名)和其女友劉穎身隐,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體唯灵,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贾铝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了埠帕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垢揩。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖敛瓷,靈堂內(nèi)的尸體忽然破棺而出叁巨,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布舞痰,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏铐伴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一鲁僚、第九天 我趴在偏房一處隱蔽的房頂上張望曼月。 院中可真熱鬧,春花似錦衙猪、人聲如沸馍乙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丝格。三九已至,卻和暖如春棵譬,著一層夾襖步出監(jiān)牢的瞬間显蝌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工订咸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留曼尊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓脏嚷,卻偏偏與公主長(zhǎng)得像骆撇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子父叙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354