ftpwebrequest 无法加载或初始化请求的服务提供程序_jvm之类加载机制
什么是類加載
每個(gè)編寫的".java"拓展名類文件都存儲(chǔ)著需要執(zhí)行的程序邏輯,這些".java"文件經(jīng)過Java編譯器編譯成拓展名為".class"的文件,".class"文件中保存著Java代碼經(jīng)轉(zhuǎn)換后的虛擬機(jī)指令,當(dāng)需要使用某個(gè)類時(shí),虛擬機(jī)將會(huì)加載它的".class"文件,并創(chuàng)建對應(yīng)的class對象,將class文件加載到虛擬機(jī)的內(nèi)存,這個(gè)過程稱為類加載。
類加載的生命周期
如圖所示,JVM類加載機(jī)制分為五個(gè)部分:加載,驗(yàn)證,準(zhǔn)備,解析,初始化。
加載
加載階段是類加載過程的第一個(gè)階段。在這個(gè)階段,JVM 的主要目的是將字節(jié)碼從各個(gè)位置(網(wǎng)絡(luò)、磁盤等)轉(zhuǎn)化為二進(jìn)制字節(jié)流加載到內(nèi)存中,接著會(huì)為這個(gè)類在 JVM 的方法區(qū)創(chuàng)建一個(gè)對應(yīng)的 Class 對象,這個(gè) Class 對象就是這個(gè)類各種數(shù)據(jù)的訪問入口。
驗(yàn)證
驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。驗(yàn)證階段大致會(huì)完成4個(gè)階段的檢驗(yàn)動(dòng)作:
- 文件格式驗(yàn)證:驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范;例如:是否以0xCAFEBABE開頭、主次版本號(hào)是否在當(dāng)前虛擬機(jī)的處理范圍之內(nèi)、常量池中的常量是否有不被支持的類型。
- 元數(shù)據(jù)驗(yàn)證:對字節(jié)碼描述的信息進(jìn)行語義分析(注意:對比javac編譯階段的語義分析),以保證其描述的信息符合Java語言規(guī)范的要求;例如:這個(gè)類是否有父類,除了java.lang.Object之外。
- 字節(jié)碼驗(yàn)證:通過數(shù)據(jù)流和控制流分析,確定程序語義是合法的、符合邏輯的。
- 符號(hào)引用驗(yàn)證:確保解析動(dòng)作能正確執(zhí)行。
驗(yàn)證階段是非常重要的,但不是必須的,它對程序運(yùn)行期沒有影響,如果所引用的類經(jīng)過反復(fù)驗(yàn)證,那么可以考慮采用-Xverifynone參數(shù)來關(guān)閉大部分的類驗(yàn)證措施,以縮短虛擬機(jī)類加載的時(shí)間。
準(zhǔn)備(重點(diǎn))
當(dāng)完成字節(jié)碼文件的校驗(yàn)之后,JVM 便會(huì)開始為類變量分配內(nèi)存并初始化。這里需要注意兩個(gè)關(guān)鍵點(diǎn),即內(nèi)存分配的對象以及初始化的類型。
- 內(nèi)存分配的對象。Java 中的變量有「類變量」和「類成員變量」兩種類型,「類變量」指的是被 static 修飾的變量,而其他所有類型的變量都屬于「類成員變量」。在準(zhǔn)備階段,JVM 只會(huì)為「類變量」分配內(nèi)存,而不會(huì)為「類成員變量」分配內(nèi)存。「類成員變量」的內(nèi)存分配需要等到初始化階段才開始。
例如下面的代碼在準(zhǔn)備階段,只會(huì)為 factor 屬性分配內(nèi)存,而不會(huì)為 website 屬性分配內(nèi)存。
public static int factor = 3; public String website = "www.baidu.com";- 初始化的類型。在準(zhǔn)備階段,JVM 會(huì)為類變量分配內(nèi)存,并為其初始化。但是這里的初始化指的是為變量賦予 Java 語言中該數(shù)據(jù)類型的零值,而不是用戶代碼里初始化的值。
例如下面的代碼在準(zhǔn)備階段之后,sector 的值將是 0,而不是 3。
public static int sector = 3;但如果一個(gè)變量是常量(被 static final 修飾)的話,那么在準(zhǔn)備階段,屬性便會(huì)被賦予用戶希望的值。例如下面的代碼在準(zhǔn)備階段之后,number 的值將是 3,而不是 0。
public static final int number = 3;兩個(gè)語句的區(qū)別是一個(gè)有 final 關(guān)鍵字修飾,另外一個(gè)沒有。而 final 關(guān)鍵字在 Java 中代表不可改變的意思,number 的值一旦賦值就不會(huì)在改變了。既然一旦賦值就不會(huì)再改變,那么就必須一開始就給其賦予用戶想要的值,因此被 final 修飾的類變量在準(zhǔn)備階段就會(huì)被賦予想要的值。而沒有被 final 修飾的類變量,其可能在初始化階段或者運(yùn)行階段發(fā)生變化,所以就沒有必要在準(zhǔn)備階段對它賦予用戶想要的值。
解析
解析階段是虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過程,解析動(dòng)作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點(diǎn)限定符7類符號(hào)引用進(jìn)行。
初始化
初始化,這個(gè)階段就是執(zhí)行類構(gòu)造器< clinit >()方法的過程,為類的靜態(tài)變量賦予正確的初始值,JVM負(fù)責(zé)對類進(jìn)行初始化,主要對類變量進(jìn)行初始化。在Java中對類變量進(jìn)行初始值設(shè)定有兩種方式:
- ①聲明類變量是指定初始值
- ②使用靜態(tài)代碼塊為類變量指定初始值
JVM初始化步驟
- 1、假如這個(gè)類還沒有被加載和連接,則程序先加載并連接該類
- 2、假如該類的直接父類還沒有被初始化,則先初始化其直接父類
- 3、假如類中有初始化語句,則系統(tǒng)依次執(zhí)行這些初始化語句
類初始化時(shí)機(jī):只有當(dāng)對類的主動(dòng)使用的時(shí)候才會(huì)導(dǎo)致類的初始化,類的主動(dòng)使用包括以下六種:
- 創(chuàng)建類的實(shí)例,也就是new的方式
- 訪問某個(gè)類或接口的靜態(tài)變量,或者對該靜態(tài)變量賦值
- 調(diào)用類的靜態(tài)方法
- 反射(如Class.forName(“com.shengsiyuan.Test”))
- 初始化某個(gè)類的子類,則其父類也會(huì)被初始化
- Java虛擬機(jī)啟動(dòng)時(shí)被標(biāo)明為啟動(dòng)類的類(Java Test),直接使用java.exe命令來運(yùn)行某個(gè)主類
使用
當(dāng) JVM 完成初始化階段之后,JVM 便開始從入口方法開始執(zhí)行用戶的程序代碼。
卸載
當(dāng)用戶程序代碼執(zhí)行完畢后,JVM 便開始銷毀創(chuàng)建的 Class 對象,最后負(fù)責(zé)運(yùn)行的 JVM 也退出內(nèi)存。
類加載器
顧名思義,類加載器(class loader)用來加載 Java 類到 Java 虛擬機(jī)中。JVM提供了3種類加載器:
啟動(dòng)類加載器:Bootstrap ClassLoader,負(fù)責(zé)加載存放在JDKjrelib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機(jī)識(shí)別的類庫(如rt.jar,所有的java.開頭的類均被Bootstrap ClassLoader加載)。啟動(dòng)類加載器是無法被Java程序直接引用的。擴(kuò)展類加載器:Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載JDKjrelibext目錄中,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(如javax.開頭的類),開發(fā)者可以直接使用擴(kuò)展類加載器。應(yīng)用程序類加載器:Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實(shí)現(xiàn),它負(fù)責(zé)加載用戶類路徑(ClassPath)所指定的類,開發(fā)者可以直接使用該類加載器,如果應(yīng)用程序中沒有自定義過自己的類加載器,一般情況下這個(gè)就是程序中默認(rèn)的類加載器。
應(yīng)用程序都是由這三種類加載器互相配合進(jìn)行加載的,如果有必要,我們還可以加入自定義的類加載器。
雙親委派模型
下圖中展示了類加載器直接的關(guān)系和雙親委派模型
從圖中我們發(fā)現(xiàn)除啟動(dòng)類加載器外,每個(gè)加載器都有父的類加載器。
雙親委派機(jī)制:如果一個(gè)類加載器在接到加載類的請求時(shí),它首先不會(huì)自己嘗試去加載這個(gè)類,而是把這個(gè)請求任務(wù)委托給父類加載器去完成,依次遞歸,如果父類加載器可以完成類加載任務(wù),就成功返回;只有父類加載器無法完成此加載任務(wù)時(shí),才自己去加載。
從類的繼承關(guān)系來看,ExtClassLoader和AppClassLoader都是繼承URLClassLoader,都是ClassLoader的子類。而BootStrapClassLoader是有C寫的,不再java的ClassLoader子類中。
從圖中可以看到類加載器間的父子關(guān)系不是以繼承的方式實(shí)現(xiàn)的,而是以組合關(guān)系的方式來復(fù)用父加載器的代碼。如果一個(gè)類加載器收到了類加載的請求,它首先會(huì)把這個(gè)請求委派給父加載器去完成,每一個(gè)層次的類加載器都是如此。 雙親委派模型的好處
Java類隨著加載它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系。比如,Java中的Object類,它存放在rt.jar之中,無論哪一個(gè)類加載器要加載這個(gè)類,最終都是委派給處于模型最頂端的啟動(dòng)類加載器進(jìn)行加載,因此Object在各種類加載環(huán)境中都是同一個(gè)類。如果不采用雙親委派模型,那么由各個(gè)類加載器自己取加載的話,那么系統(tǒng)中會(huì)存在多種不同的Object類。
jvm類加載相關(guān)鏈接:
https://blog.csdn.net/javazejian/article/details/73413292
https://www.jianshu.com/p/3cab74a189de
https://www.fangzhipeng.com/javainterview/2019/04/15/class-loader.html
http://www.ityouknow.com/jvm/2017/08/19/class-loading-principle.html
https://www.cnblogs.com/chanshuyi/p/the_java_class_load_mechamism.html
https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html
總結(jié)
以上是生活随笔為你收集整理的ftpwebrequest 无法加载或初始化请求的服务提供程序_jvm之类加载机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDFS文件目录操作代码
- 下一篇: Django模板语言中的自定义方法fil