欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)激活碼778899分享:JVM類加載

柚子快報(bào)激活碼778899分享:JVM類加載

http://yzkb.51969.com/

1、JVM是什么?

2、JDK,JRE,JVM關(guān)系

JDK包含JRE以及相關(guān)的工具和工具API JRE包含JVM

3、JVM該如何學(xué)習(xí)

(1)java源代碼到類文件 (2)類文件到JVM (3)JVM各種對類文件進(jìn)行處理【內(nèi)部結(jié)構(gòu),執(zhí)行方式,垃圾回收,本地調(diào)用】

4、源碼到類文件

4.1、前期編譯

java源文件 -> 詞法分析器 -> token流 -> 語法分析器 -> 語法樹/抽象語法樹 -> 語義分析器 -> 注解抽象語法樹 -> 字節(jié)碼生成器 -> Class文件

4.2、類文件

4.3 類文件到虛擬機(jī)(類加載機(jī)制)

類加載機(jī)制是指我們將類的字節(jié)碼文件所包含的數(shù)據(jù)讀入內(nèi)存,同時(shí)我們會生成數(shù)據(jù)的訪問入口的一種特殊機(jī)制,那么我們可以得知,類加載的最終產(chǎn)品是數(shù)據(jù)訪問入口 這個(gè)時(shí)候,看到這張圖,我們應(yīng)該有一個(gè)問題,那就是我們的字節(jié)碼加載的方式,也就是我們的字節(jié)碼文件可以用什么方式進(jìn)行加載呢?

4.3.1 加載.class文件方式

從本地系統(tǒng)中直接加載通過網(wǎng)絡(luò)下載.class文件

典型場景:Web Applet,也就是我們的小程序應(yīng)用

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

典型場景:后續(xù)演變?yōu)閖ar,war格式

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

典型場景:JSP應(yīng)用從專有數(shù)據(jù)庫中提取.class文件,較為少見

將java源文件動態(tài)編譯為.class文件

典型場景:動態(tài)代理技術(shù)

從加密文件中獲取

典型場景:典型的防Class文件被反編譯的保護(hù)措施

4.3.2 類加載機(jī)制流程

所謂類加載機(jī)制就是

虛擬機(jī)把class文件加載到內(nèi)存 并對數(shù)據(jù)進(jìn)行校驗(yàn),轉(zhuǎn)換解析和初始化 形成可以虛擬機(jī)直接使用的java類型,即Java.lang.Class

4.3.2.1 裝載

查找和導(dǎo)入class文件

1. 通過一個(gè)類的全限定名獲取定義此類的二進(jìn)制字節(jié)流 2. 將這個(gè)字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu) 3. 在java堆中生成一個(gè)代表這個(gè)類的java.lang.Class對象,作為對方法去中這些數(shù)據(jù)的訪問入口 獲取類的二進(jìn)制字節(jié)流的階段是我們java程序員最關(guān)注的階段,也是操作性最強(qiáng)的一個(gè)階段,因?yàn)檫@個(gè)階段我們可以在我們的裝載階段完成之后,這個(gè)時(shí)候在我們的內(nèi)存當(dāng)中,我們的運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法去以及堆就可以有數(shù)據(jù)了

方法區(qū):類信息,靜態(tài)變量,常量堆:代表被加載類的java.lang.Class對象 即時(shí)編譯之后的熱點(diǎn)代碼并不在這個(gè)階段進(jìn)入方法區(qū)

4.3.2.2 鏈接

驗(yàn)證

驗(yàn)證主要是為了確保Class文件中的字節(jié)流包含的信息完全符合當(dāng)前虛擬機(jī)的要求,并且還要求我們的信息不會危害虛擬機(jī)自身的安全,導(dǎo)致虛擬機(jī)崩潰

文件格式驗(yàn)證

驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理,該驗(yàn)證的主要目的是保證輸入的字節(jié)流能正確地解析并存儲與方法去之內(nèi),這個(gè)階段的驗(yàn)證是基于二進(jìn)制字節(jié)流進(jìn)行的,只有經(jīng)過該階段的驗(yàn)證后,字節(jié)流才會進(jìn)入內(nèi)存的方法區(qū)中進(jìn)行存儲,后面驗(yàn)證都是基于方法區(qū)的存儲結(jié)構(gòu)進(jìn)行的

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

對類的元數(shù)據(jù)信息進(jìn)行語義校驗(yàn),其實(shí)就是對java語法校驗(yàn),保證不存在不符合java于法規(guī)定的元數(shù)據(jù)信息

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

進(jìn)行數(shù)據(jù)流和控制流分析,確定程序語義是合法的,符合邏輯的,對類的方法體進(jìn)行校驗(yàn)分析,保證被校驗(yàn)的類的方法在運(yùn)行時(shí)不會做出危害虛擬機(jī)安全的行為,獲取類的二進(jìn)制字節(jié)流的階段是我們java程序員最關(guān)注的階段,也是操作性最強(qiáng)的階段,因?yàn)檫@個(gè)階段我們可以對于我們的類加載器進(jìn)行操作,比如我們想自定義類加載器進(jìn)行操作用以完成加載,又或者我們想通過java Agent來完成我們的字節(jié)碼增強(qiáng)操作

符號引用驗(yàn)證

這是最后一個(gè)階段的驗(yàn)證,它發(fā)生在虛擬機(jī)將符號引用轉(zhuǎn)換為直接引用的時(shí)候(解析階段),可以看作是對類自身以外的信息(常量池中的各種符號引用)進(jìn)行匹配性的校驗(yàn),符號引用驗(yàn)證的目的是確保解析動作能正常執(zhí)行

注意:我們很多情況下可能認(rèn)為我們的代碼肯定沒有問題,驗(yàn)證的過程完全沒有必要,那么其實(shí)我們可以添加參數(shù) -Xverify:none 取消驗(yàn)證

準(zhǔn)備

為類的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值 這里不包含用final修飾的static,因?yàn)閒inal在編譯的時(shí)候就會分配了,準(zhǔn)備階段會顯式初始化; 這里不會為實(shí)例變量(也就是沒加static)分配初始化,類變量會分配在方法區(qū)中,而實(shí)例變量是會隨著對象一起分配到j(luò)ava堆中的

進(jìn)行分配內(nèi)存的只是包括類變量(靜態(tài)變量),而不包括實(shí)例變量,實(shí)例變量實(shí)在對象實(shí)例化是隨著對象一起分配在java堆中的,通常情況下,初始化為默認(rèn)值,假設(shè)public static int a = 1; 那么a在準(zhǔn)備階段之后的初始值0,不為1,這時(shí)候只是開辟了內(nèi)存空間,并沒有運(yùn)行java代碼,a賦值為1的指令是程序被編譯后,存放于類構(gòu)造器()方法中,所以a賦值為1是初始化階段才會執(zhí)行

思考:ConstantValue屬性到底是干什么的呢?

ConstantValue屬性的作用是通知虛擬機(jī)自動為靜態(tài)變量賦值,只有被static修飾的變量才可以使用這項(xiàng)屬性,非static類型的變量的賦值是在實(shí)力構(gòu)造器中進(jìn)行的;static類型變量賦值分兩種,在類構(gòu)造其中賦值,或者使用ConstantValue屬性賦值

思考:在實(shí)際的程序中,我們?yōu)槭裁床艜玫紺ontstanValue屬性呢?

在實(shí)際的程序中,只有同時(shí)被final和static修飾的字段才有ConstantValue屬性,且限于基本類型和String,編譯時(shí)javac將會為該廠里生成ConstantValue屬性,在類加載的準(zhǔn)備階段虛擬機(jī)便會根據(jù)ConstantValue的常量設(shè)置相應(yīng)的值,如果該變量沒有被final修飾,或者并且基本類型及字符串嗎,則選擇在類構(gòu)造器中進(jìn)行初始化

思考:為什么ConstantValue的屬性值只限于基本類型和String

因?yàn)閺某A砍刂兄荒芤玫交绢愋秃蚐tring類型的字面量 舉個(gè)例子:假設(shè)上面的類變量a被定義為:private static final int a = 1; 編譯時(shí)javac將會為a生成ConstantValue屬性,在準(zhǔn)備階段虛擬機(jī)就會根據(jù)ConstantValue的設(shè)置將value賦值為1,我們可以理解static final常量在編譯期就將其結(jié)果放入了調(diào)用它的類的常量池中

解析 把類中的符號引用轉(zhuǎn)換為直接引用

符號引用就是一組符號來描述目標(biāo),可以是任何字面量 直接引用是直接指向目標(biāo)的指針,相對偏移量或一個(gè)間接定位到目標(biāo)的句柄

解析階段是虛擬機(jī)將常量池內(nèi)的符號引用替換為直接引用的過程, 解析動作主要是針對類或接口,字段,類方法,接口方法,方法類型,方法句柄和調(diào)用限定符7類符號引用進(jìn)行

直接引用是與虛擬機(jī)內(nèi)存布局實(shí)現(xiàn)相關(guān),同一個(gè)符號引用在不同虛擬機(jī)實(shí)例上翻譯出來的直接引用一般不會相同,如果有了直接引用,那么引用的目標(biāo)必定存在內(nèi)存中

對解析結(jié)果進(jìn)行緩存 同一符號引用進(jìn)行多次解析請求是很常見的,出invokedynamic指令以外,虛擬機(jī)實(shí)現(xiàn)可以堆第一次解析結(jié)果進(jìn)行緩存,來避免解析動作重復(fù)進(jìn)行,無論是否真正執(zhí)行了多次解析動作,虛擬機(jī)需要保證的是在同一個(gè)實(shí)體中,如果一個(gè)引用符號之前已經(jīng)被成功解析過,那么后續(xù)的引用解析請求就應(yīng)當(dāng)一直成功,同樣的,如果第一次解析失敗,那么其他指令對這個(gè)符號的解析請求也應(yīng)該收到相同的異常 inDy是java7引入的一條新的虛擬機(jī)指令,這是自1.0以來第一次引入新的虛擬機(jī)執(zhí)行,到了java8這條指令第一次在java應(yīng)用,用在lambda表達(dá)式中,indy與其他invoke指令不同的是他允許由應(yīng)用級的代碼來決定方法解析

4.3.2.3 初始化

初始化階段是執(zhí)行類構(gòu)造器()方法的過程 或者講的通俗易懂,在準(zhǔn)備階段,類變量已經(jīng)父之過一次系統(tǒng)要求的初始值,而在初始化階段,則是根據(jù)程序員通過程序指定的主觀計(jì)劃去初始化類變量和其他資源,比如賦值

在java中對類變量進(jìn)行初始值設(shè)定有兩種方式

聲明類變量時(shí)指定初始值使用靜態(tài)代碼塊為類變量指定初始值

按照程序員的邏輯,你必須把靜態(tài)變量定義在靜態(tài)代碼塊的前面,因?yàn)閮蓚€(gè)的執(zhí)行是會根據(jù)代碼編寫的順序來決定的,順序搞錯(cuò)了可能會影響你的業(yè)務(wù)代碼

jvm初始化步驟

假如這個(gè)類還沒有被加載和鏈接,則程序先加載并鏈接該類假如該類的直接父類還沒有被初始化,則先初始化其直接父類假如類中有初始化語句,則系統(tǒng)依次執(zhí)行這些初始化語句

4.3.2.4 使用

那么這個(gè)時(shí)候我們?nèi)ニ伎家粋€(gè)問題,我們的初始化過程什么時(shí)候會被觸發(fā)呢? 或者換句話來說類初始化時(shí)機(jī)是什么呢?

主動引用

只有當(dāng)對類的主動使用的時(shí)候才會導(dǎo)致類的初始化,類的主動使用由有六種:

創(chuàng)建類的實(shí)例,也就是new的方式訪問某個(gè)類或接口的靜態(tài)變量,或者對該靜態(tài)變量賦值調(diào)用類的靜態(tài)方法反射初始化某個(gè)類的子類,則其父類也會被初始化java虛擬機(jī)啟動時(shí)會標(biāo)明為啟動類的類,直接使用java.exe命令來運(yùn)行某個(gè)主類

被動引用

引用父類的靜態(tài)字段,只會引起父類的初始化,而不會引起子類的初始化定義類數(shù)組,不會引起類的初始化引用類的static final常量,不會引起類的初始化(如果只有static修飾,還是會因?yàn)樵擃惖某跏蓟?

4.3.2.5 卸載

在類使用完之后,如果滿足下面的情況,類就會被卸載

該類所有的實(shí)例都已經(jīng)被回收,也就是java堆中不存在該類的任何實(shí)例加載該類的ClassLoader已經(jīng)被回收該類對應(yīng)的java.lang,Class對象沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法

java虛擬機(jī)本身會使用引用這些類加載器,而這些類加載器則會使用引用他們所加載的類的Class對象,因?yàn)檫@些Class對象始終是可觸及的

如果以上三個(gè)條件全部滿足,jvm就會在方法區(qū)垃圾回收的時(shí)候?qū)︻愡M(jìn)行卸載,類的卸載過程其實(shí)就是在方法區(qū)中清空類信息,java類的整個(gè)生命周期就結(jié)束了,但是一般情況下啟動類加載器加載的類不會被卸載,而我們的其他兩種基礎(chǔ)類型的類加載器只有在極少數(shù)情況下才會被卸載

4.3.3 類加載器ClassLoader

在裝載階段,其中通過類的全限定名獲取其定義的二進(jìn)制字節(jié)流,需要借助類裝載器完成,顧名思義,就是用來裝載Class文件的

4.3.3.1 什么是類加載器?

負(fù)責(zé)讀取java字節(jié)代碼,并轉(zhuǎn)換成java.lang.Class類的一個(gè)實(shí)例的代碼模塊類加載器除了用于加載類外,還可用于確定類在java虛擬機(jī)中的唯一性

一個(gè)類在同一個(gè)類加載器中具有唯一性,而不同類加載器中允許同名類存在的,這里的同名是指全限定名相同,但是在整個(gè)JVM里,從然全限定名相同,若類加載器不同,則仍然不算做是同一個(gè)類,無法通過instanceOf,equals等方式的校驗(yàn)

4.3.3.2 分類

Bootstrap ClassLoader 負(fù)責(zé)加載$JAVA_HONE中jre/lib/rt.jar里面所有的class或Xbootclassoath選項(xiàng)指定的jar包Extension ClassLoader 負(fù)責(zé)加載java平臺中擴(kuò)展功能的一些jar包,包括$JAVA_HONE中jre/lib/*.jar或-Djava.eApp ClassLoader 負(fù)責(zé)加載classpath中指定的jar包以及Djava.class.path所指定目錄下的類和jar包Custom ClassLoader 通過java.lang.classLoader的子類自定義加載class,屬于應(yīng)用程序根據(jù)自身需要自定的類加載器

為什么我們的類加載器要分層? 1.2版本的jvm中,只有一個(gè)類加載器,就是現(xiàn)在的Bootstarp類加載器,也就是根類加載器,但是這樣會出現(xiàn)一個(gè)問題 假如用戶調(diào)用它編寫的java.lang.String類,理論上該類可以訪問和改變java.lang包下其他類的默認(rèn)訪問修飾符的屬性和方法的能力,也就是說,我們其他類使用String時(shí)也會調(diào)用這個(gè)類,因?yàn)橹挥幸粋€(gè)類加載器,我無法判定到底加載哪個(gè),因?yàn)閖ava語言本身并沒有阻止這種行為,所有會出現(xiàn)問題 這個(gè)時(shí)候,我們就想到,可不可以使用不同級別的類加載器來對我們信任級別做一個(gè)分區(qū)呢? 比如用三種基礎(chǔ)的類加載器作為我們的三種不同的信任級別,最可信的級別是java核心API類,然后是安裝的拓展類,最后才是類路徑中的類 所以,我們?nèi)N基礎(chǔ)類的加載器由此而生

JVM類加載機(jī)制的三種特性

全盤負(fù)責(zé)當(dāng)一個(gè)類加載器負(fù)責(zé)加載某個(gè)Class時(shí),該Class所依賴的和引用的其他Class也將由該類加載器負(fù)責(zé)載入,除非顯式使用另外一個(gè)類加載器宅如

例如,系統(tǒng)類加載器AppClassLoader加載入口類時(shí),會把main方法所依賴的類及引用的類也載入,以此類推,"全盤負(fù)責(zé)"機(jī)制也可以成為當(dāng)前類加載器負(fù)責(zé)機(jī)制。顯然,入口類所依賴的類及引用的類的當(dāng)前類加載器就是入口類的加載器

以上步驟只是調(diào)用ClassLoader.loadClass(name)方法,并沒有真正定義類,整整加載class字節(jié)碼文件生成Class對象由雙親委派機(jī)制完成

父類委托 父類委托也叫雙親委派機(jī)制,是指子類加載器如果沒有加載過該目標(biāo)類,就先委托父類加載器加載該目標(biāo)類,只有在父類加載器找不到字節(jié)碼文件的情況下才從自己的類路徑中查到并裝載目標(biāo)類

父類委托別名就叫做雙親委派機(jī)制 雙親委派機(jī)制加載Class具體的過程 1、ClassLoader先判斷該Class是否已加載,如果已加載,則返回Class對象,如果沒有則委托給父類加載器 2、父類加載器判斷是否加載過該Class,如果已加載,則返回Class對象,如果沒有則委托給祖父類加載器 3、以此類推,直到始祖類加載器 4、始祖類加載器判斷是否加載過該Class,如果以加載,則返回Class對象,如果沒有則嘗試從其對應(yīng)的類路徑下尋找class字節(jié)碼文件并載入,如果載入成功,則返回Class對象,如果載入失敗,則委托給始祖類加載器的子類加載器 5、始祖類加載器的子類加載器嘗試從其對應(yīng)的類路徑下尋找class字節(jié)碼文件并載入,如果載入成功,則返回class對象,如果載入失敗,則委托給始祖類加載器的孫類加載器 6、依此類推,直接源ClassLoader 7、源ClassLoader嘗試從其對應(yīng)的類路徑下尋找class字節(jié)碼文件并載入,如果載入成功,則返回Class對象,如果載入失敗,源ClassLoader不會在委托其子類加載器,而是拋出異常

緩存機(jī)制 緩存機(jī)制將會保證所有加載過的Class都將在內(nèi)存中緩存,當(dāng)程序中需要使用某個(gè)Class時(shí),類加載器先從內(nèi)存的緩存區(qū)尋找該Class,只有緩存區(qū)不存在,系統(tǒng)才會讀取該類對應(yīng)的二進(jìn)制數(shù)據(jù),并將其轉(zhuǎn)換成Class對象,存入緩存區(qū),這就是為什么修改了Class后,必須重啟JVM,程序的修改才會生效,對于一個(gè)類加載器實(shí)例來說,相同的全名的類只加載一次,即loadClass方法不會被重復(fù)調(diào)用

而這里我們JDK8使用的時(shí)直接內(nèi)存,所以我們會用到直接內(nèi)存進(jìn)行緩存,這也就是我們類變量為什么只會被初始化一次的由來

打破雙親委派 雙親委派這個(gè)模型并不是強(qiáng)制模型,而且會帶來一些問題,就比如java.sql.Driver這個(gè)東西,JDK只能提供一個(gè)規(guī)范接口,而不能提供是實(shí)現(xiàn),提供實(shí)現(xiàn)的是實(shí)際的數(shù)據(jù)庫提供商,提供商的庫總不能放在JDK目錄中 所以java想到幾種辦法可以打破我們的雙親委派 SPI:比如java從1.6搞出了SPI就是為了優(yōu)雅的解決這類問題------JDK提供接口,供應(yīng)商提供服務(wù),編程人員編碼時(shí)面向接口編碼,然后JDK能夠自動找到合適的實(shí)現(xiàn)

java在核心類庫中定義了許多接口,并且還給出了這些接口的調(diào)用邏輯,然而未給出實(shí)現(xiàn),開發(fā)者要做的就是定制一個(gè)實(shí)現(xiàn)類,在META-INF/services中注冊實(shí)現(xiàn)類信息,以供核心類庫使用,比如JDBC中的DriverManager

OSGI: 比如我們的java程序員更加追求程序的動態(tài)性,比如代碼熱部署,代碼熱替換,也就是機(jī)器不用重啟,只要不部署上就能用,OSGI實(shí)現(xiàn)模塊化熱部署的關(guān)鍵則是它自定義的類加載器機(jī)制實(shí)現(xiàn)的,每一個(gè)程序模塊都有自己的類加載器,當(dāng)需要更換一個(gè)程序模塊時(shí),就把程序模塊連同類加載器一起換掉以實(shí)現(xiàn)代碼的熱替換

自定義類加載器 自定義類加載器的核心在于對字節(jié)碼文件的獲取,如果是加密的字節(jié)碼則需要在該類中對文件進(jìn)行解密,有幾點(diǎn)需要注意:

這里傳遞的文件名需要時(shí)類的全限定名,因?yàn)閐efifineClass方法是按照Test格式進(jìn)行處理的

如果沒有全限定名,那么我們需要做的事情就是將類的全路徑加載進(jìn)去,而我們的setRoot就是前綴地址setRoot + loadClass的路徑就是文件的絕對路徑

最好不要重寫loadClass方法,因?yàn)檫@樣容易破壞雙親委派模式這類Test本身可以被AppClassLoader類加載,因此我們不能把Test.class放在類路徑下,否則,由于雙親委派機(jī)制的存在,會導(dǎo)致該類由AppClassLoader加載,而不會通過我們自定義的加載器來加載

柚子快報(bào)激活碼778899分享:JVM類加載

http://yzkb.51969.com/

相關(guān)鏈接

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19055905.html

發(fā)布評論

您暫未設(shè)置收款碼

請?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄