一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

深入理解JVM類加載器

 侯培彬 2018-04-20

類加載器

類加載的第一個階段就需要通過一個類的全限定名來獲取描述此類的二進(jìn)制字節(jié)流,實(shí)現(xiàn)這個動作的模塊就是類加載器。

類加載器雖然只是實(shí)現(xiàn)類的加載動作,但是在Java程序中的作用遠(yuǎn)不止于此。在Java中一個類的唯一性不僅僅是看類本身,還要看它的加載器。通俗地說:比較兩個類是否相等,只有在兩個類時由同一個類加載器加載的前提下才有意義,否則,即使兩個類來源于同一個Class文件,被同一個虛擬機(jī)加載,只要類加載器不同,兩個類也不相等。

下面看周志明老師那本書上例舉的一段代碼:

import java.io.IOException; import java.io.InputStream; public class ClassLoaderTest { public static void main(String[] args) throws Exception{ ClassLoader myLoader = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf('.') 1) '.class'; InputStream is = getClass().getResourceAsStream(fileName); if (is==null) { return super.loadClass(name); } byte[] b =new byte[is.available()]; is.read(b); return defineClass(name,b,0,b.length); } catch (IOException e) { throw new ClassNotFoundException(name); } } }; Object obj = myLoader.loadClass('ClassLoaderTest').newInstance(); System.out.println(obj.getClass()); //class ClassLoaderTest System.out.println(obj instanceof ClassLoaderTest); //false } }java學(xué)習(xí)交流群:478052716

注釋后打印了輸出內(nèi)容,我們使用自己定義的類加載器去加載本身產(chǎn)生的class文件,產(chǎn)生Class類,再實(shí)例化產(chǎn)生了obj對象。從打印的第一句看出,obj確實(shí)是ClassLoaderTest實(shí)例化的對象,但第二句返回了false,因?yàn)橄到y(tǒng)內(nèi)存在兩個ClassLoaderTest類,一個是系統(tǒng)應(yīng)用程序類加載器加載的,一個是我們自定義的類加載器加載的,雖然來自于同一個class文件,但是卻是兩個類。

上面我們提到了應(yīng)用程序加載器,自定義類加載器什么的,別急,繼續(xù)往下看。

雙親委派模型

什么是雙親委派模型想必也是面試中的常問考點(diǎn)。之前我們也提到過在JVM中,只存在兩種類加載器:

  • 啟動類加載器(Bootstrap ClassLoader): C 實(shí)現(xiàn),虛擬機(jī)的一部分

  • 用戶自定義類加載器(User-Defined Class Loader): Java語言實(shí)現(xiàn),獨(dú)立于虛擬機(jī)外部,并且都是繼承與java.lang.ClassLoader

一般來說,在討論類加載器時,我們會劃分的更細(xì),我們可以看下圖:

深入理解JVM類加載器

類加載器

下面的討論都將基于這幅圖。

絕大部分的Java程序都會使用到以下3種系統(tǒng)提供的類加載器。

  • 啟動類加載器(Bootstrap ClassLoader): 這個類將負(fù)責(zé)把<JAVA_HOME>\lib\目錄中的,或者-Xbootclasspath參數(shù)指定的目錄所指定的路徑中的,并且是虛擬機(jī)識別的的類庫加載到虛擬機(jī)內(nèi)存中,如rt.jar,識別僅按照文件名識別,如果名字不符合,即使在這個目錄下,也不會被加載。啟動類加載器無法被java程序直接引,用戶如果在編寫自定義的類加載器時,如果需要把加載請求委托給引導(dǎo)類加載器,那么直接用null代替即可。

  • 擴(kuò)展類加載器(Extension ClassLoader): 這個類加載器由sun.misc.Launcher $ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統(tǒng)變量所指定的路徑中的所有類庫。

  • 應(yīng)用程序加載類(Application ClassLoader): 這個類加載器是由sun.misc.Launcher $App-ClassLoader實(shí)現(xiàn)。該加載器是由ClassLoader的getSystemClassLoader()方法返回,所以一般稱它為系統(tǒng)類加載器。一般它加載用戶類路徑(ClassPath)所指定的類庫,開發(fā)者一般直接使用這個類加載器,如果沒有定義自己的類加載器,那么這個應(yīng)用程序加載類就是程序中默認(rèn)的類加載器。

我們來解釋下什么是雙親委派模型?

雙親委派模型要求除了頂層的Bootstrap ClassLoader外,其它所有類加載器都要有自己的父類加載器。這里的父子關(guān)系一般不會議繼承實(shí)現(xiàn),而是通過組合實(shí)現(xiàn)。它的基本工作流程是:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器完成,每個層次的類加載器都是如此,因此最后所有的請求都會傳遞到頂層的啟動類加載器中,只有當(dāng)父加載器返回自己無法完成這個加載請求(即它的搜索范圍內(nèi)沒有找到所需要的類),子加載器才會嘗試去自己加載。

使用雙親委派模型的好處呢?

使用雙親委派模型最直接的好處就是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系,例如jaba.lang.Object類,它存放在rt.jar中,無論哪一個類加載器要加載這個類,最終都要委派給最上層的boostrap ClassLoader,所以O(shè)bject類在程序的各種類加載器環(huán)境中都是同一個類。假如沒有使用雙親委派模型,由各個類各自加載Object,那么系統(tǒng)里將會出現(xiàn)各種版本的Object類,導(dǎo)致整個系統(tǒng)的混亂。

下面我們從ClassLoader的源碼來看看雙親委派模式:

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); //檢查該類是否加載過了 if (c == null) {//沒加載過的情況 long t0 = System.nanoTime(); try { if (parent != null) { //如果自定義的類加載器的parent不為null,就調(diào)用parent的loadClass進(jìn)行加載類 c = parent.loadClass(name, false); } else { //否則就去找bootstrap ClassLoader c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }

從上面的代碼來看,使用指定名稱加載類分為以下3步:

  1. 調(diào)用findLoadedClass(String)來檢查類是否已經(jīng)被加載;

  2. 調(diào)用父類的loadClass方法,如果父類為空,就調(diào)用虛擬機(jī)內(nèi)置的引導(dǎo)類加載器加載;

  3. 調(diào)用findClass(String)來查找該類。

因?yàn)?code>loadClass封裝了雙親委派模型,所以在開發(fā)自己的類加載器時,Java標(biāo)準(zhǔn)提覆寫findClass()方法。

通常情況下,Java虛擬機(jī)都是從文件系統(tǒng)里load一個class,但是有一些類不一定來自一個文件,它們也可能來自別的源,比如網(wǎng)絡(luò),加密文件等等,假設(shè)我們寫一個自己的類加載器,加載服務(wù)器下載的class文件。

示例使用代碼:

ClassLoader loader = new NetworkClassLoader(host, port); Object main = loader.loadClass('Main', true).newInstance();

在定義時,我們覆寫findClass方法:

class NetworkClassLoader extends ClassLoader { String host; int port; public Class findClass(String name) { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); } private byte[] loadClassData(String name) { // load the class data from the connection . . . } }

這里重要的還有個defineClass函數(shù),用來把一組二進(jìn)制字節(jié)轉(zhuǎn)換為Class的實(shí)例,轉(zhuǎn)換為Class后再交給后續(xù)的類加載過程解析。后續(xù)步驟就又回到深入理解JVM類加載機(jī)制中所描述的了。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    伊人欧美一区二区三区| 91福利免费一区二区三区| 国产欧美日韩精品自拍| 成人精品网一区二区三区| 久久永久免费一区二区| 日韩中文字幕人妻精品| 微拍一区二区三区福利| 日韩国产传媒在线精品| 青青久久亚洲婷婷中文网| 噜噜中文字幕一区二区| 大尺度剧情国产在线视频| 亚洲一区二区欧美激情| 又大又长又粗又黄国产| 日本不卡在线视频中文国产 | 国产一区二区精品高清免费| 欧美色欧美亚洲日在线| 国产一区二区久久综合| 精品女同在线一区二区| 精品人妻少妇二区三区| 日韩人妻毛片中文字幕| 后入美臀少妇一区二区| 日本在线 一区 二区| 久草国产精品一区二区| 亚洲第一区欧美日韩在线| 亚洲高清亚洲欧美一区二区| 99久久国产精品亚洲| 国产又粗又猛又黄又爽视频免费| 中文字日产幕码三区国产| 久久老熟女一区二区三区福利| 国产综合欧美日韩在线精品| 亚洲另类欧美综合日韩精品| 国产精品白丝一区二区| 久久国产精品热爱视频| 久久黄片免费播放大全| 欧美日不卡无在线一区| 最新午夜福利视频偷拍| 千仞雪下面好爽好紧好湿全文| 国产精品涩涩成人一区二区三区| 亚洲中文字幕视频在线播放| 九九热国产这里只有精品| 日韩中文字幕免费在线视频|