日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > > 架構(gòu)師社區(qū)
[導(dǎo)讀]這篇文章不聊別的,專(zhuān)門(mén)來(lái)侃侃JVM的類(lèi)加載機(jī)制 概念 類(lèi)加載器把class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,存放在方法區(qū),然后在堆區(qū)創(chuàng)建一個(gè)java.lang.Class對(duì)象,用來(lái)封裝類(lèi)在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)。 一、加載: 查找并加載類(lèi)的二進(jìn)制數(shù)據(jù)(把class文件里面的信息


這篇文章不聊別的,專(zhuān)門(mén)來(lái)侃侃JVM的類(lèi)加載機(jī)制


概念

類(lèi)加載器把class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中,存放在方法區(qū),然后在堆區(qū)創(chuàng)建一個(gè)java.lang.Class對(duì)象,用來(lái)封裝類(lèi)在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)。

一、加載:
查找并加載類(lèi)的二進(jìn)制數(shù)據(jù)(把class文件里面的信息加載到內(nèi)存里面)

二、連接:
把內(nèi)存中類(lèi)的二進(jìn)制數(shù)據(jù)合并到虛擬機(jī)的運(yùn)行時(shí)環(huán)境中

  1. 驗(yàn)證:確保被加載的類(lèi)的正確性,包括:

    1. 類(lèi)文件的結(jié)構(gòu)檢查:檢查是否滿足Java類(lèi)文件的固定格式

    2. 語(yǔ)義檢查:確保類(lèi)本身符合Java的語(yǔ)法規(guī)范

    3. 字節(jié)碼驗(yàn)證:確保字節(jié)碼流可以被Java虛擬機(jī)安全的執(zhí)行。字節(jié)碼流是操作碼組成的序列。每一個(gè)操作碼后面都會(huì)跟著一個(gè)或者多個(gè)操作數(shù)。字節(jié)碼檢查這個(gè)步驟會(huì)檢查每一個(gè)操作碼是否合法。

    4.  二進(jìn)制兼容性驗(yàn)證:確保相互引用的類(lèi)之間是協(xié)調(diào)一致的。

  2. 準(zhǔn)備:為類(lèi)的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值

  3. 解析:把類(lèi)中的符號(hào)引用轉(zhuǎn)化為直接引用(比如說(shuō)方法的符號(hào)引用,是由方法名和相關(guān)描述符組成。在解析階段,JVM把符號(hào)引用替換成一個(gè)指針,這個(gè)指針就是直接引用,它指向該類(lèi)的該方法在方法區(qū)中的內(nèi)存位置)

三、初始化:

為類(lèi)的靜態(tài)變量賦予正確的初始值。當(dāng)靜態(tài)變量的等號(hào)右邊的值是一個(gè)常量表達(dá)式時(shí),不會(huì)調(diào)用static代碼塊進(jìn)行初始化。只有等號(hào)右邊的值是一個(gè)運(yùn)行時(shí)運(yùn)算出來(lái)的值,才會(huì)調(diào)用static初始化。

雙親委派模型

1、將類(lèi)加載請(qǐng)求向上傳遞。當(dāng)一個(gè)類(lèi)加載器收到類(lèi)加載請(qǐng)求時(shí),它首先不會(huì)自己去加載這個(gè)類(lèi)的信息,而是把該請(qǐng)求轉(zhuǎn)發(fā)給父類(lèi)加載器,依次向上。

所以所有的類(lèi)加載請(qǐng)求都會(huì)被傳遞到父類(lèi)加載器中,只有當(dāng)父類(lèi)加載器中沒(méi)有找到所需的類(lèi),子類(lèi)加載器才會(huì)自己嘗試去加載該類(lèi)。

當(dāng)前類(lèi)加載器和所有父類(lèi)加載器都無(wú)法加載該類(lèi)時(shí),拋出ClassNotFindException異常。

2、意義:提高系統(tǒng)的安全性。用戶(hù)自定義的類(lèi)加載器不可能加載應(yīng)該由父加載器加載的可靠類(lèi)。

比如用戶(hù)定義了一個(gè)惡意代碼,自定義的類(lèi)加載器首先讓系統(tǒng)加載器去加載,系統(tǒng)加載器檢查該代碼不符合規(guī)范,于是就不繼續(xù)加載了。

3、定義類(lèi)加載器:如果某個(gè)類(lèi)加載器能夠加載一個(gè)類(lèi),那么這個(gè)類(lèi)加載器就叫做定義類(lèi)加載器

4、初始類(lèi)加載器:定義類(lèi)加載器及其所有子加載器都稱(chēng)作初始類(lèi)加載器。

5、運(yùn)行時(shí)包:

  • 由同一個(gè)類(lèi)加載器加載并且擁有相同包名的類(lèi)組成運(yùn)行時(shí)包

  • 只有屬于同一個(gè)運(yùn)行時(shí)包的類(lèi),才能訪問(wèn)包可見(jiàn)(default)的類(lèi)和類(lèi)成員。

  • 其作用是限制用戶(hù)自定義的類(lèi)冒充核心類(lèi)庫(kù)的類(lèi)去訪問(wèn)核心類(lèi)庫(kù)的包可見(jiàn)成員

6、加載兩份相同class對(duì)象的情況:A 和 B 不屬于父子類(lèi)加載器關(guān)系,并且各自都加載了同一個(gè)類(lèi)。

特點(diǎn):

  1. 全盤(pán)負(fù)責(zé):當(dāng)一個(gè)類(lèi)加載器加載一個(gè)類(lèi)時(shí),該類(lèi)所依賴(lài)的其他類(lèi)也會(huì)被這個(gè)類(lèi)加載器加載到內(nèi)存中。

  2. 緩存機(jī)制:所有的Class對(duì)象都會(huì)被緩存,當(dāng)程序需要使用某個(gè)Class時(shí),類(lèi)加載器先從緩存中查找,找不到,才從class文件中讀取數(shù)據(jù),轉(zhuǎn)化成Class對(duì)象,存入緩存中。

兩種類(lèi)型的類(lèi)加載器

1、 JVM自帶的類(lèi)加載器(3種)

(1) 根類(lèi)加載器(Bootstrap):

a、C++編寫(xiě)的,程序員無(wú)法在程序中獲取該類(lèi)
b、負(fù)責(zé)加載虛擬機(jī)的核心庫(kù),比如java.lang.Object
c、沒(méi)有繼承ClassLoader類(lèi)

(2) 擴(kuò)展類(lèi)加載器(Extension):

a、Java編寫(xiě)的,從指定目錄中加載類(lèi)庫(kù)
b、父加載器是根類(lèi)加載器
c、是ClassLoader的子類(lèi)
d、如果用戶(hù)把創(chuàng)建的jar文件放到指定目錄中,也會(huì)被擴(kuò)展加載器加載。

(3) 系統(tǒng)加載器(System)或者應(yīng)用加載器(App):

a、Java編寫(xiě)的
b、父加載器是擴(kuò)展類(lèi)加載器
c、從環(huán)境變量或者class.path中加載類(lèi)
d、是用戶(hù)自定義類(lèi)加載的默認(rèn)父加載器
e、是ClassLoader的子類(lèi)

2、 用戶(hù)自定義的類(lèi)加載器:
(1)Java.lang.ClassLoader類(lèi)的子類(lèi)
(2)用戶(hù)可以定制類(lèi)的加載方式
(3)父類(lèi)加載器是系統(tǒng)加載器
(4)編寫(xiě)步驟:

A、繼承ClassLoader
B、重寫(xiě)findClass方法。從特定位置加載class文件,得到字節(jié)數(shù)組,然后利用defineClass把字節(jié)數(shù)組轉(zhuǎn)化為Class對(duì)象

(5)為什么要自定義類(lèi)加載器? 

  1. 可以從指定位置加載class文件,比如說(shuō)從數(shù)據(jù)庫(kù)、云端加載class文件

  2. 加密:Java代碼可以被輕易的反編譯,因此,如果需要對(duì)代碼進(jìn)行加密,那么加密以后的代碼,就不能使用Java自帶的ClassLoader來(lái)加載這個(gè)類(lèi)了,需要自定義ClassLoader,對(duì)這個(gè)類(lèi)進(jìn)行解密,然后加載。

PS:

問(wèn)題:Java程序?qū)︻?lèi)的執(zhí)行有幾種方式:

1、 主動(dòng)使用(6種情況):
// JVM必須在每個(gè)類(lèi)“首次主動(dòng)使用”的時(shí)候,才會(huì)初始化這些類(lèi)。

(1) 創(chuàng)建類(lèi)的實(shí)例
(2) 讀寫(xiě)某個(gè)類(lèi)或者接口的靜態(tài)變量
(3) 調(diào)用類(lèi)的靜態(tài)方法
(4) 同過(guò)反射的API(Class.forName())獲取類(lèi)
(5) 初始化一個(gè)類(lèi)的子類(lèi)
(6) JVM啟動(dòng)的時(shí)候,被標(biāo)明啟動(dòng)類(lèi)的類(lèi)(包含Main方法的類(lèi))

// 只有當(dāng)程序使用的靜態(tài)變量或者靜態(tài)方法確實(shí)在該類(lèi)中定義時(shí),該可以認(rèn)為是對(duì)該類(lèi)或者接口的主動(dòng)使用。

2、 被動(dòng)使用:除了主動(dòng)使用的6種情況,其他情況都是被動(dòng)使用,都不會(huì)導(dǎo)致類(lèi)的初始化。

3、 JVM規(guī)范允許類(lèi)加載器在預(yù)料某個(gè)類(lèi)將要被使用的時(shí)候,就預(yù)先加載它。

如果該class文件缺失或者存在錯(cuò)誤,則在程序“首次 主動(dòng)使用”時(shí),才報(bào)告這個(gè)錯(cuò)誤(Linkage Error錯(cuò)誤)

如果這個(gè)類(lèi)一直沒(méi)有被程序“主動(dòng)使用”,就不會(huì)報(bào)錯(cuò)。

類(lèi)加載機(jī)制與接口:

  1. 當(dāng)Java虛擬機(jī)初始化一個(gè)類(lèi)時(shí),不會(huì)初始化該類(lèi)實(shí)現(xiàn)的接口。

  2.  在初始化一個(gè)接口時(shí),不會(huì)初始化這個(gè)接口父接口。

  3. 只有當(dāng)程序首次使用該接口的靜態(tài)變量時(shí),才導(dǎo)致該接口的初始化。

ClassLoader:調(diào)用Classloader的loadClass方法去加載一個(gè)類(lèi),不是主動(dòng)使用,因此不會(huì)進(jìn)行類(lèi)的初始化。

類(lèi)的卸載:

  1. 有JVM自帶的三種類(lèi)加載器(根、擴(kuò)展、系統(tǒng))加載的類(lèi)始終不會(huì)卸載。因?yàn)镴VM始終引用這些類(lèi)加載器,這些類(lèi)加載器使用引用他們所加載的類(lèi),因此這些Class類(lèi)對(duì)象始終是可到達(dá)的。

  2. 由用戶(hù)自定義類(lèi)加載器加載的類(lèi),是可以被卸載的。


作者:Huangy遠(yuǎn) ,本文版權(quán)歸作者所有

https://segmentfault.com/a/1190000016415939


特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:

Java 工程師必須掌握的 JVM 類(lèi)加載機(jī)制!

Java 工程師必須掌握的 JVM 類(lèi)加載機(jī)制!

Java 工程師必須掌握的 JVM 類(lèi)加載機(jī)制!

長(zhǎng)按訂閱更多精彩▼

Java 工程師必須掌握的 JVM 類(lèi)加載機(jī)制!

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉