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

分享

JVM類加載機制(ClassLoader)源碼解析

 橘子悅讀 2013-11-18

http://blog.csdn.net/chenyi8888/article/details/7066569

其實JVM類加載機制,簡單地說就是類管理,也就是我們生成的class文件。
三個步驟:裝載(load)、鏈接(link)、解析(Resolve)、還有初始化(Initialize)
關(guān)于網(wǎng)上有很多講解加載的方式,和調(diào)用的方式,還是幾個基本的classLoader,這里就不在多描述了。
這里更多的是從源碼上來講解,達(dá)到理論結(jié)合實際。

首先是ClassLoader這個抽象類,這個是實現(xiàn)自定義類的基礎(chǔ)。那么在調(diào)用的時候,首先都是調(diào)用loadClass這個方法,如圖:

該方法調(diào)用的是一個重載方法loadClass(name,false)方法如圖:

第一行注釋很清楚的表示,從當(dāng)前的類加載器里加載class文件,如果已經(jīng)加載,就直接返回;不存在就加載;如果返回NULL,表示class文件不在這個類加載器的范圍內(nèi)。
這是就會執(zhí)行if(c==null)下面的方法,就會去找父加載器(類加載器的整個結(jié)構(gòu)是樹形的,這里不多介紹),如果父加載器不存在,就直接找到根加載器(根加載器一定存在)。
我們現(xiàn)在開始解析每個被調(diào)用方法。
首先看下findLoadedClass方法,如圖:

該方法首先,校驗類名是否正確(類似于 test.demo.Test.class),校驗成功后,調(diào)用本地方法findLoadedClass0方法。

接著是parent.loadClass(name,false)方法,該方法是重復(fù)的,只是對象不同而已,就不介紹了。
接著是findBootstrapClassOrNull方法,如圖:

方式與findLoadedClass方法一樣,就是本地調(diào)用是findBootstrapClass方法。
如果此時類還不存在,有一個findClass方法的調(diào)用。代碼如圖:

這里看上去覺得很奇怪,直接就拋一個空異常了,其實根據(jù)注釋所說,就是自定義的類加載器需要實現(xiàn)這種方法。
為什么要實現(xiàn)這個方法呢?原因就是有些場景會需要通過網(wǎng)絡(luò)傳輸?shù)姆绞竭M(jìn)行加載類(該類其實是在遠(yuǎn)程服務(wù)器上的),在很多地方就會用到,例如遠(yuǎn)程調(diào)用等技術(shù)。
具體的實現(xiàn)例子,在URLClassLoader里就有體現(xiàn),這里暫不介紹。

最后是調(diào)用了resolveClass方法,代碼如圖:

這個方法很簡單,主要是重新進(jìn)行解析類,該解析主要是對所有的屬性/方法調(diào)用是否存在、相應(yīng)的權(quán)限(如private、public等)進(jìn)行驗證。那么為什么默認(rèn)都是傳入false呢?
那是因為是否需要更強的安全機制的檢測,
另一個情況是類加載是無序的,會導(dǎo)致類鏈接不成功。
這里有個狀態(tài)位的控制,主要是可能有些場景需要更嚴(yán)格的對類進(jìn)行驗證(目前我還沒有使用到這樣的場景)。

這里要注意的是:
類為什么是樹形結(jié)構(gòu),主要就是安全,因為類加載器都是從不同的目錄進(jìn)行加載的(網(wǎng)上有介紹三個最基礎(chǔ)的類加載器Bootstrap ClassLoader\Extension ClassLoader\System ClassLoader加載的默認(rèn)目錄,這里不多介紹),所以用這種目錄的方式來進(jìn)行權(quán)限管理,常用都是使用classpath系統(tǒng)變量,如果我想自己定義一個加載目錄,那么就需要實現(xiàn)自己的類加載器,進(jìn)行相應(yīng)的權(quán)限管理。
另外這個抽象類,實現(xiàn)了只是從本地進(jìn)行加載類的方式,如果需要進(jìn)行遠(yuǎn)程加載類,那么也需要實現(xiàn)自己的類加載器。

這里是類加載器的基本功能介紹,里面還有如何加載native Library 方法、class文件/包名相關(guān)安全驗證等,在后續(xù)會繼續(xù)介紹。

 

 

JVM類加載機制(ClassLoader)源碼解析(2)

分類: java服務(wù)器 2011-12-13 15:21 4726人閱讀 評論(0)收藏 舉報

我們來對defineClass這個方法進(jìn)行解析,該方法比較復(fù)雜,首先如圖:

該方法主要是通過一個字節(jié)的數(shù)組,對該數(shù)據(jù)進(jìn)行解析、驗證、二進(jìn)制碼格式校驗。通過拋出的ClassFormatError的這個異常來看,也是驗證這個類的格式是否符合JVM的規(guī)范。
最終是將一個字節(jié)數(shù)組轉(zhuǎn)換成類實例(注意不是實例對象,是有區(qū)別的)。
目前該方法已被替代,注意上圖里的注釋。

目前新方法,如下圖:

第一個參數(shù),其實就是類名(包括整個包名在內(nèi))。
現(xiàn)在對該方法進(jìn)行詳細(xì)的解析,首先看下check方法,如圖:

非常簡單,就是檢測classLoader是否初始化。

preDefineClass方法,如圖:

checkName檢測類名是否有效(去除數(shù)組類型的判斷),禁止以java.為前綴包名。如果沒有保護(hù)域,則獲取默認(rèn)保護(hù)域;最后是進(jìn)行數(shù)字簽名的驗證。
主要看下數(shù)字驗證方法,checkCerts方法,如圖:

數(shù)字驗證的方式也很簡單,如果存在就比較當(dāng)前保護(hù)域里的數(shù)字簽名與之前的數(shù)字簽名是否匹配;反之就在package2certs對象(就是一個hashtable)里緩存起來。
這里的數(shù)字簽名是從CodeSource這個類型的對象來獲取,也就是代碼源里的簽名。
compareCerts方法就是對比兩個數(shù)字簽名是否匹配(方法很簡單,這里就不用截圖了)。這里主要是針對于類安全考慮,防止有人惡意修改類文件,采用的是通用的java安全框架來實現(xiàn)的。以后會在java安全的源碼分析里在進(jìn)行詳細(xì)介紹。

defineClassSourceLocation方法,如圖:

非常簡單,就是獲取代碼基的路徑。
然后就是開始調(diào)用本地解析方法defineClass1進(jìn)行處理(類似于findLoadedClass方法處理)。如果拋出ClassFormatError異常,在給一次機會(調(diào)用方法defineTransformedClass),重新傳輸一次類的二進(jìn)制碼做解析;如果還是失敗,就拋出ClassFormatError異常類型。

最后是執(zhí)行postDefineClass方法,如圖:

這里就是用之前比較過的數(shù)字簽名,對類進(jìn)行簽名操作(也就是類似于加密操作)

這里需要注意的是:
defineClass方法與loadClass方法是兩種不同加載類的實現(xiàn)。
區(qū)別在于:
前者是有嚴(yán)格的安全機制,輸入源是一個二進(jìn)制碼;后者很簡單就是一個本機的加載方式,輸入源來自本地存放的class文件。
前者比較適合于網(wǎng)絡(luò)遠(yuǎn)程加載類,因為需要進(jìn)行安全控制;后者是一個比較基于自定義目錄加載的實現(xiàn)。
前者在實現(xiàn)時主要是findClass方法與之配合使用;而后者主要是要設(shè)置加載的URL(也就是路徑)。
這里非常明顯地體現(xiàn)出了java的核心思想(嚴(yán)格地來講應(yīng)該是核心特性),安全和網(wǎng)絡(luò)。

 

JVM類加載機制(ClassLoader)源碼解析(3)

分類: java服務(wù)器 2011-12-13 19:00 4743人閱讀 評論(0)收藏 舉報

java的類加載器,還有一個特殊的功能,就是加載本地庫。這個功能是與關(guān)鍵字native是有關(guān)系的。簡單地說就是調(diào)用C++/C的本地庫(windows是后綴為.dll,linux下是后綴為.so)。

調(diào)用的地方是使用System這個類,其中有兩個方法如下:
load(String filename)
loadLibrary(String libname)
一個是根據(jù)文件名,一個根據(jù)lib名。注意文件名不等同于lib名,不然寫錯了,會報加載失敗信息。
System類是一個很特殊的類,與底層交互較多,會看到很多native關(guān)鍵字,到時候在會在解析System和Runtime時再詳細(xì)介紹。

回到之前的主題,這兩個方法最終都是調(diào)用到了ClassLoader.loadLibrary(fromClass, name,isAbsolute)。該方法如圖:


說明下參數(shù):
fromClass這個參數(shù)是當(dāng)前調(diào)用者的類實例,例如:
public class Test{
            public void load(){
                   System.loadLibrary("1232");
            }
}
這時fromClass參數(shù)就是Class<Test>。
isAbsolute參數(shù)很簡單,指是否是規(guī)則路徑(例如:../../test.java)。
name參數(shù)有可能是文件名方式,也有可能是libname方式。

該方法第一階段就是基于DownloadManager判斷,如果JRE沒有完成并且當(dāng)前還有線程下載時,就會調(diào)用DownloadManager類的靜態(tài)方法downloadFile。
DownloadManager類,這個屬于sun.jkernel包下的,與JRE組件有關(guān),簡單地說就是安裝了JRE的存放路。
有時候JRE是在嵌入的位置就是JAVA_HOME/jre,有時候是獨立位置,特別是在windows操作系統(tǒng)下安裝時,會提示安裝JRE這時可以隨時選擇存放路徑。
JVM初始化完成后會調(diào)用此類,另外DownloadManager這個類也支持命令行的操作。以后會進(jìn)行更多的詳細(xì)介紹。

因為較為復(fù)雜,很多用了調(diào)用本地方法,所以我這里只簡單介紹。
java.library.path是java的系統(tǒng)變量,其值等于系統(tǒng)變量里的PATH。
sun.boot.library.path是java系統(tǒng)變量,其值等于JAVA_HOME/jre/bin。

主要是通過4個過程來加載本地庫。
第1步驟就是判斷是否是規(guī)則路徑,為true,就直接對規(guī)則路徑進(jìn)行轉(zhuǎn)換后加載,如果加載失敗直接拋出異常并執(zhí)行步驟5;反之執(zhí)行步驟2。
                規(guī)則路徑就是使用了.或..這樣符號。
第2步驟如果類加載器不為空,從類加載里獲取libname信息,
               但是抽象類ClassLoader是一直返回NULL的(可查看ClassLoader.findLibrary方法),所以基本上都會直接執(zhí)行步驟3(除非是自定義類加載器,重寫了此方法)。
第3步驟首先從sun.boot.library.path里的全部路徑下搜索libname進(jìn)行加載,如果成功執(zhí)行步驟5;反之執(zhí)行步驟4。
第4步驟首先判斷類加載器是否為空,不為空時,將從java.library.path里的全部路徑下搜索libname進(jìn)行加載,不管成功與否,都會執(zhí)行步驟5。
第5步驟執(zhí)行完畢。

   另外本地庫名稱(已經(jīng)加載過的)在代碼中有三個存放屬性,如下:
    // All native library names we've loaded.
    private static Vector loadedLibraryNames = new Vector();
    // Native libraries belonging to system classes.
    private static Vector systemNativeLibraries = new Vector();
    // Native libraries associated with the class loader.
    private Vector nativeLibraries = new Vector();


加載之后存放相關(guān)信息使用NativeLibrary類來保存的,該加載機制與JNI技術(shù)有密切聯(lián)系,也使用下JNI會對該加載功能有更深入的了解。

System與Runtime源碼解析

分類: java服務(wù)器 2011-12-14 14:13 4095人閱讀 評論(0) 收藏 舉報

在類加載器里提到了System與Runtime類,這里就趁熱打鐵來對這兩個源碼進(jìn)行解析,因為System與Runtime關(guān)聯(lián)很緊密,所以就一起來解析吧。
首先來看看System類提供的幾個特性:
1、standard input, standard output, and error output streams
2、訪問擴展屬性和java的環(huán)境變量
3、加載本地內(nèi)庫
4、提供一個arraycopy的復(fù)制功能
5、獲取Console對象
6、獲取和設(shè)置SecurityManager對象
7、獲取本地庫文件mapLibraryName方法
      該方法示例:System.out.println(System.mapLibraryName("awt")); 打印結(jié)果為awt.dll,這個文件存放的路徑在JAVA_HOME/jre/bin目錄下,大家可以自己試試其他的。
8、JVM退出(exit)

看下Runtime類提供的幾個特性:
1、一個JVM對應(yīng)一個Runtime對象(single)
2、允許訪問和調(diào)用其他應(yīng)用程序
3、擴展ShutdownHook
4、獲取內(nèi)存使用相關(guān)信息
5、加載本地內(nèi)庫
6、JVM退出(exit)

首先分析下System類,該類有個重要的方法,如下圖:

該方法是被JVM調(diào)用的,很奇怪吧。當(dāng)時我也沒想明白,這個方法是private,怎么被調(diào)用到呢?后來仔細(xì)分析后發(fā)現(xiàn),注意這個方法,如下:
private static native void registerNatives();
    static {
        registerNatives();
    }
該方法會將initializeSystemClass方法映射到本地方法里,方便JVM調(diào)用;這里很重要,JNI方便java去調(diào)用C++/C的動態(tài)連接庫。而該方法是讓C++/C能調(diào)用到j(luò)ava方法。
至于registerNatives方法做了什么更具體的事情,可以去查看源碼。
我們來具體分析下initalizeSystemClass做那些事情:
1、初始化out、in、err等流
2、初始化環(huán)境變量Properties
3、初始化信號量,Terminator.setup();
4、sun.misc.VM.initializeOSEnvironment();
       
       OSEnvironment代碼如下:
       
       這里就很明顯了,主要是設(shè)置些錯誤模式標(biāo)識,JVM如何處理這樣的錯誤(目前是四種:臨界區(qū)錯誤、文件錯誤、自動修復(fù)內(nèi)存對齊、一般的故障保護(hù))。
       
5、sun.misc.VM.maxDirectMemory();
       該方法在VM很簡單,就是直接return directMemory,該參數(shù)的設(shè)置與-XX:MaxDirectMemorySize=<size>有關(guān)
6、sun.misc.VM.allowArraySyntax();
       在VM也很簡單,也是直接返回return allowArraySyntax
7、sun.misc.VM.booted();
       在VM里是將booted賦值為true。

個人認(rèn)為5、6兩個步驟是沒有必要的,不知道為什么要調(diào)用下。
另外就是如何驗證initalizeSystemClass是JVM調(diào)用的,很簡單,可以參考下out/in/err等屬性是如何初始化的。我自己寫了模擬例子,如下:
class AB{
}

final class AB_System{
public static final AB a=nullAB();

private static AB nullAB(){
if (System.currentTimeMillis() > 0) {
   return null;
}
throw new NullPointerException();
}

static{
System.out.println("ab_system static");
}
private AB_System(){
}
public static void print(){
System.out.println("abaddd_dadfa");
}
}
public class Test_System {
public static void main(String[] args) {
System.out.println(AB_System.a==null);
}
}
運行結(jié)果是true。

再來分析下Runtime類。
因源碼里有較多的native方法,所以邏輯比較簡單,
這里就是主要是exec這樣的方法,可以找些例子操作體驗下,就行了。

與之相關(guān)的時Process類,這個類就是外部被調(diào)用的應(yīng)用程序,在java里的一個代理對象(也可以叫做交互的接口對象,例如返回相關(guān)的運行狀態(tài)等),后面會對這個類進(jìn)行解析。

還有就是掌握強制終止和正常終止的區(qū)別。

關(guān)于兩者之間的關(guān)聯(lián)
個人認(rèn)為差別不是很大,因為有很多相同點(System里也有需要調(diào)用Runtime里的方法)。
分成兩個類,還是因為職責(zé)分開吧,System比較傾向于程序員使用(是一個工具類,不能被實例化),而Runtime(是一個single object)更傾向于與JVM和其他應(yīng)用程序交互。
這兩個類主要用于的場景如下:
如何安全的關(guān)閉應(yīng)用程序(換句話說就是java程序退出或者JVM退出),可能很少在寫java程序時(特別是java的開源項目眾多),要考慮如何關(guān)閉JVM的
自定義的SecurityManager類
增加Hook
加載自己的動態(tài)鏈接庫等

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    精品国产av一区二区三区不卡蜜| 亚洲午夜av一区二区| 麻豆看片麻豆免费视频| 大香蕉精品视频一区二区| 日本久久中文字幕免费| 亚洲免费视频中文字幕在线观看| 丰满少妇被粗大猛烈进出视频| 午夜资源在线观看免费高清| 九九热在线视频精品免费| 午夜国产成人福利视频| 亚洲一区二区三区中文久久| 婷婷亚洲综合五月天麻豆| 日韩中文字幕有码午夜美女| 国产综合一区二区三区av| 中文字幕一二区在线观看| 精品久久av一二三区| 免费大片黄在线观看国语| 久久99亚洲小姐精品综合 | 成人精品一区二区三区综合| 精品日韩中文字幕视频在线| 亚洲一区二区三区三州| 国产色第一区不卡高清| 99久久人妻精品免费一区| 欧美日韩国产成人高潮| 日本人妻精品中文字幕不卡乱码 | 空之色水之色在线播放| 成人区人妻精品一区二区三区| 欧美三级不卡在线观线看| 国产精品免费福利在线| 日本精品最新字幕视频播放| 日韩精品免费一区三区| 国产精品免费不卡视频| 免费性欧美重口味黄色| 日本道播放一区二区三区| 黄色片国产一区二区三区| 中文字幕亚洲精品人妻| 国产免费自拍黄片免费看| 日韩一区二区三区在线欧洲| 欧美一级特黄大片做受大屁股| 毛片在线观看免费日韩| 精品国产亚洲一区二区三区|