天天看點

Java虛拟機如何加載Class檔案

前面兩篇部落格,我詳細的講解下Java主動記憶體管理的一些情況,如果想要更深入的了解Java虛拟機的運作機制的話,我建議可以閱讀下《深入了解Java虛拟機》這本書。而這篇部落客要記錄的是Java虛拟機是如何把.class檔案加載到記憶體,具體的加載過程是怎麼樣的呢?請看下面的詳細講解。 首先,必須得明白類加載機制大緻原理:虛拟機把描述類的資料從CLASS檔案加載到記憶體,并對資料進行校驗,轉換解析和初始化,最終形成可以被虛拟機直接使用的Java類型。 再次,必須得記住類加載的時機。類加載的生命周期:加載、驗證、準備、解析、初始化、使用和解除安裝; 連接配接包括:驗證、準備和解析; !

Java虛拟機如何加載Class檔案

2)類初始化(立即)條件:加載、驗證、準備已結束 A )遇到new, getstatic, putstatic, invokestatic指令時,如果累沒有進行初始化,則需要先觸發其初始化;對應的場景:使用new執行個體化對象;讀取或設定類的靜态字段;調用類的靜态方法;   B)使用java.lang.reflect包的方法對類進行反射的時候;   C)當初始化一個類的時候,如果發現其父類沒有初始化,需要先觸發父類初始化;   D)當虛拟機啟動時,要執行的主類,虛拟機會先初始化主類; 注意:(1)常量在編譯期存入調用的常量池中,本質上沒有直接引用到定義常量的類,是以不會觸發定義常量的類的初始化。 下面再好好講下加載類每一步,虛拟機做了哪些事: 一、加載   1)通過一個類的全限定名來擷取定義此類的二進制位元組流;   2)将這個位元組流所代表的靜态存儲結構轉化為方法區的運作時資料結構;   3)在記憶體中生成一個代表類的java.lang.Class對象,作為方法區類的各種資料的通路入口; 二、驗證(連接配接的第一步) 1)目的:確定Class檔案的位元組流中包含的資訊符合目前虛拟機的要求,并不會危害虛拟機自身的安全;   2)步驟:A)檔案格式驗證:驗證位元組流是否符合Class檔案格式規範,并且能被目前版本的虛拟機處理; B)中繼資料驗證:對位元組碼描述的資訊進行語義分析,保證描述的資訊符合java語言規範要求; eg:此類是否有父類;這個類的父類是否繼承了不允許被繼承的類;   C)位元組碼驗證:最複雜;對類的方法體進行校驗分析;   D)符合引用驗證: **三、準備(指派)**   1 )目的:為類變量配置設定記憶體并設定類變量初始值;類變量(被static修飾的變量)在方法區中配置設定;執行個體變量:在對象執行個體化時随對象一起配置設定到Java堆中; **四、解析**   1)目的:将常量池内的符号引用替換為直接引用的過程; **五、初始化(類加載最後一步)**:執行**類構造器<clinit>()方法**的過程 1)<clinit>()方法:由編譯器自動收集類中的所有類變量的指派動作和靜态語塊(static {})中的語句合并産生的;收集的順序是由語句在源檔案中出現的順序; **靜态語句塊隻能通路到定義在靜态語句塊之前的變量,定義在它之後的變量,在前面的靜态語句塊可以指派,但是不能通路; ** 2)<clinit>()方法與類的構造函數(執行個體構造器<init>()方法)差別: **<clinit>()方法不需要顯式調用父類構造器,VM會保證子類的<clinit>()方法執行之前,父類的<clinit>()方法已經執行完畢; ** 3)父類的<clinit>()方法先執行:**父類中的定義的靜态語句塊要優先于子類的變量指派操作。 ** 4)**<clinit>()方法不是必需的**,如果類沒有靜态語句塊,也沒對變量的指派操作,那麼編譯器不為類生成<clinit>()方法; 5)接口中不能使用靜态語句塊,但接口有變量的初始化指派操作,是以接口和類都會生成<clinit>()方法;但執行接口的<clinit>()方法不需要先執行父接口<clinit>()方法; 6)VM保證一個類的<clinit>方法在多線程中被正确加鎖、同步。 **常見的一些類加載器的分類(雙親委派模型): ** 1)虛拟機角度來看,兩種不同的類加載器:   A)啟動類加載器(Bootstrap ClassLoader):由C++實作,是VM的一部分;   B)所有其他的類加載器:由java實作,獨立于VM外部;繼承抽象類java.lang.ClassLoader; 2)程式員角度來看,可分為三種不同的類加載器:   A)啟動類加載器(Bootstrap ClassLoader):負責将<JAVA_HOME>\lib目錄中的,或被Xbootclasspath 參數所指定的路徑中,并且是虛拟機識别的類庫加載到記憶體中;無法被Java程式直接引用;在需要使用自 己加載器時,需要把啟動類加載器賦null;   B)擴充類加載器(Extension ClassLoader):負責加載<JAVA_HOME>\lib\ext目錄中的,或被java.ext.dirs系統變量所指定的路徑中的所有類庫;可直接使用擴充類加載器;   C)應用程式類加載器(Application ClassLoader):此加載器時ClassLoader中的getSystemClassLoader()方法的傳回值,系統類加載器;負責加載使用者路徑(ClassPath)上所指定的類庫;可直接使用此類加載器;程式預設的類加載器;