標籤:執行 lex 自訂 定義類 lang exce 過程 分類 c++
先瞭解一下:
什麼是類的載入:jvm將class文讀取到記憶體中,經過對class檔案的校正、轉換解析、初始化最終在jvm的heap和方法區分配記憶體形成可以被jvm直接使用的類型的過程。
生命週期:7個階段依次為:Loading(載入)Verification (驗證)Preparation (準備)Resolution(串連) Initialization (初始化)Using(使用) Unloading(卸載)
載入Loading
這個階段jvm完成以下動作:
首先 類載入器通過類的全路徑限定名讀取類的二進位位元組流,
然後 將二進位位元組流代表的類結構轉化到運行時資料區的 方法區中,
最後 在jvm堆中產生代表這個類的java.lang.Class執行個體(不是這個類的執行個體)
類載入器
擷取類的二進位流 既可以使用jvm內建的類載入器,也可以自己寫載入器來載入,這一小步是完全可控的。不同的載入器可以從各種地方讀取:zip包jar包,class檔案,網路流 。。。讀取類的二進位位元組流
同一個載入器載入的同源類才是真的同類。不同載入器載入同源類,不是同類!instanceof為FALSE
類載入的雙親委派模型
各個載入器都是先委託自己的父載入器載入類,若確實沒載入到再自己來載入
於是java預設的類尋找載入順序是自頂向下的,樹狀結構
雙親委託的意圖是保證java類型體系中最基礎的行為一致,優先載入JDK中的類
載入器主要有四種:
jvm啟動類載入器bootstrap loader,用c++實現為jvm的一部分(僅指sun的hotspot),負責 JAVA_HOME/lib下面的類庫中的類的載入,這個載入器,java程式無法引用到。
擴充類載入器Extension Loader,由sun.misc.Launcher$ExtClassLoader類實現,可在java中使用,負責JAVA_HOME/lib/ext 目錄和java.ext.dir目錄中類庫的類的載入。
應用系統類別載入器Application System Loader,由sun.misc.Louncher$AppClassLoader實現,負責載入使用者類路徑中類庫中的類,如果沒有使用自訂的載入器,這個就是預設的 載入器!
使用者自訂載入器 自己定義從哪裡載入類的二進位流
1,類載入的過程Java程式啟動並執行場所是記憶體,當在命令列下執行:java HelloWorld命令的時候,JVM會將HelloWorld.class載入到記憶體中,並形成一個Class的對象HelloWorld.class。其中的過程就是類載入過程:1、尋找jre目錄,尋找jvm.dll,並初始化JVM;2、產生一個Bootstrap Loader(啟動類載入器);3、Bootstrap Loader自動載入Extended Loader(標準擴充類載入器),並將其父Loader設為Bootstrap Loader。4、Bootstrap Loader自動載入AppClass Loader(系統類別載入器),並將其父Loader設為Extended Loader。5、最後由AppClass Loader載入HelloWorld類。2,類載入器各自搜尋的目錄Bootstrap、 ExtClassLoader、 AppClassLoader都是類載入器,Bootstrap是本地代碼編寫的,而ExtClassLoader、 AppClassLoader都是都java編寫的,都在rt.jar中。1、Bootstrap Loader(啟動類載入器):載入System.getProperty("sun.boot.class.path")所指定的路徑或jar。2、Extended Loader(標準擴充類載入器ExtClassLoader):載入System.getProperty("java.ext.dirs")所指定的路徑或jar。在使用Java運行程式時,也可以指定其搜尋路徑,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld3、AppClass Loader(系統類別載入器AppClassLoader):載入System.getProperty("java.class.path")所指定的路徑或jar。在使用Java運行程式時,也可以加上-cp來覆蓋原有的Classpath設定,例如: java -cp ./lavasoft/classes HelloWorld ExtClassLoader和AppClassLoader在JVM啟動後,會在JVM中儲存一份,並且在程式運行中無法改變其搜尋路徑。如果想在運行時從其他搜尋路徑載入類,就要產生新的類載入器。3,類載入器的特點1、運行一個程式時,總是由AppClass Loader(系統類別載入器)開始載入指定的類。2、在載入類時,每個類載入器會將載入任務上交給其父,如果其父找不到,再由自己去載入。3、Bootstrap Loader(啟動類載入器)是最頂級的類載入器了,其父載入器為null.4,類載入器的擷取public class HelloWorld { public static void main(String[] args) { HelloWorld hello = new HelloWorld(); Class c = hello.getClass(); ClassLoader loader = c.getClassLoader(); System.out.println(loader); System.out.println(loader.getParent()); System.out.println(loader.getParent().getParent()); } }運行結果:[email protected][email protected]null從上面的結果可以看出,並沒有擷取到ExtClassLoader的父Loader,原因是Bootstrap Loader(啟動類載入器)是用C語言實現的,找不到一個確定的返回父Loader的方式,於是就返回null。5,類的載入類載入有三種方式:1、命令列啟動應用時候由JVM初始化載入2、通過Class.forName()方法動態載入3、通過ClassLoader.loadClass()方法動態載入 三種方式區別比較大,看個例子就明白了:public class HelloWorld { public static void main(String[] args) throws ClassNotFoundException { ClassLoader loader = HelloWorld.class.getClassLoader(); System.out.println(loader); //使用ClassLoader.loadClass()來載入類,不會執行初始化塊 loader.loadClass("Test2"); //使用Class.forName()來載入類,預設會執行初始化塊 // Class.forName("Test2"); //使用Class.forName()來載入類,並指定ClassLoader,初始化時不執行靜態塊 // Class.forName("Test2", false, loader); } } public class Test2 { static { System.out.println("靜態初始化塊執行了!"); } } 分別切換載入方式,會有不同的輸出結果。6,自訂ClassLoad為了說明問題,先看例子:package test; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; public class MyClassLoader { public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException { URL url = new URL("file:/E:\\projects\\testScanner\\out\\production\\testScanner"); ClassLoader myloader = new URLClassLoader(new URL[]{url}); Class c = myloader.loadClass("test.Test3"); System.out.println("----------"); Test3 t3 = (Test3) c.newInstance(); } } public class Test3 { static { System.out.println("Test3的靜態初始化塊執行了!"); } } 運行後:---------- Test3的靜態初始化塊執行了! Process finished with exit code 0 可以看出自訂了ClassLoader myloader = new URLClassLoader(new URL[]{url});已經成功將類Test3載入到記憶體了,並通過預設構造方法構造了對象Test3 t3 = (Test3) c.newInstance(); 有關ClassLoader還有很重要一點:同一個ClassLoader載入的類檔案,只有一個Class執行個體。但是,如果同一個類檔案被不同的ClassLoader載入,則會有兩份不同的ClassLoader執行個體(前提是著兩個類載入器不能用相同的父類載入器)。
OSGi的網狀載入模型
雙親委派是java設計者推薦的類載入器實現方式,可以在遵循的基礎上擴充,自訂類載入器的實現機制。
OSGi事實上的java模組化標準,他自訂的類載入器,能很多好實現模組化和模組的熱部署:更換一個bundle時,連同這個bundle的類載入器一同換掉。
OSGi中java.*開頭的類按照雙親載入機制載入,而其他類則都是由平級的類載入器載入的,形成一張網。
驗證verification
Loading和 驗證是交叉進行的,驗證二進位位元組流代表的位元組碼檔案是否合格,主要從一下幾方面判斷:
檔案格式:參看class檔案格式詳解,經過檔案格式驗證之後的位元組流才能進入方法區分配記憶體來儲存。
中繼資料驗證:是否符合java語言規範
位元組碼驗證:資料流和控制流程的分析,這一步最複雜
符號引用驗證:符號引用轉化為直接引用時(解析階段),檢測對類自身以外的資訊進行存在性、可訪問性驗證
如果確認代碼安全無誤,可用 -Xverify:none關閉大部分類的驗證,加快類載入時間
準備preparation
在方法區中給類的類變數(static修飾)分配記憶體
然後初始化其值,如果類變數是常量,則直接賦值為該常量值否則為java類型的預設的零值。
解析resolution
指將常量池內的符號引用替換為直接引用的過程。
初始化initialization
這個階段才真正開始執行java代碼,靜態代碼塊和設定變數的初始值為程式員設定的值
主動引用
有且只有下面5種情況才會立即初始化類,稱為主動引用:
new 對象時
讀取或設定類的靜態欄位(除了 被final,已在編譯期把結果放入常量池的 靜態欄位)或調用類的靜態方法時;
用java.lang.reflect包的方法對類進行反射調用沒初始化過的類時
初始化一個類時發現其父類沒初始化,則要先初始化其父類
含main方法的那個類,jvm啟動時,需要指定一個執行主類,jvm先初始化這個類
其他對類的引用 稱為被動引用,載入類時不會進行初始化動作
子類繼承父類時的初始化順序
1.首先初始化父類的static變數和塊,按出現順序
2.初始化子類的static變數和塊,按出現順序
3.初始化父類的普通變數,調用父類的建構函式
4.初始化子類的普通變數,調用子類的建構函式
類的初始化過程發生時刻:
1. T是一個類,當T的一個執行個體建立的時候,也就是T t = new T();
2. T的一個靜態方法被調用的時候,也就是 T.staticField();
3. T的靜態屬性被賦值的時候,T.staticField = o;
4. T的一個靜態屬性被使用的時候,也就是 Object o = T.staticField; 但是它不是常量。
5. T is a top level class , and an assert statement lexically nested
1,類載入的過程Java程式啟動並執行場所是記憶體,當在命令列下執行:java HelloWorld命令的時候,JVM會將HelloWorld.class載入到記憶體中,並形成一個Class的對象HelloWorld.class。其中的過程就是類載入過程:1、尋找jre目錄,尋找jvm.dll,並初始化JVM;2、產生一個Bootstrap Loader(啟動類載入器);3、Bootstrap Loader自動載入Extended Loader(標準擴充類載入器),並將其父Loader設為Bootstrap Loader。4、Bootstrap Loader自動載入AppClass Loader(系統類別載入器),並將其父Loader設為Extended Loader。5、最後由AppClass Loader載入HelloWorld類。2,
類載入器各自搜尋的目錄
Bootstrap、 ExtClassLoader、 AppClassLoader都是類載入器,Bootstrap是本地代碼編寫的,而ExtClassLoader、 AppClassLoader都是都java編寫的,都在rt.jar中。
1、Bootstrap Loader(啟動類載入器):載入System.getProperty("sun.boot.class.path")所指定的路徑或jar。2、Extended Loader(標準擴充類載入器ExtClassLoader):載入System.getProperty("java.ext.dirs")所指定的路徑或jar。在使用Java運行程式時,也可以指定其搜尋路徑,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld3、AppClass Loader(系統類別載入器AppClassLoader):載入System.getProperty("java.class.path")所指定的路徑或jar。在使用Java運行程式時,也可以加上-cp來覆蓋原有的Classpath設定,例如: java -cp ./lavasoft/classes HelloWorld ExtClassLoader和AppClassLoader在JVM啟動後,會在JVM中儲存一份,並且在程式運行中無法改變其搜尋路徑。如果想在運行時從其他搜尋路徑載入類,就要產生新的類載入器。3,類載入器的特點1、運行一個程式時,總是由AppClass Loader(系統類別載入器)開始載入指定的類。2、在載入類時,每個類載入器會將載入任務上交給其父,如果其父找不到,再由自己去載入。
3、Bootstrap Loader(啟動類載入器)是最頂級的類載入器了,其父載入器為null.4,類載入器的擷取
public class HelloWorld {
public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
Class c = hello.getClass();
ClassLoader loader = c.getClassLoader();
System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
}
運行結果:
[email protected]
[email protected]
null
從上面的結果可以看出,並沒有擷取到ExtClassLoader的父Loader,原因是Bootstrap Loader(啟動類載入器)是用C語言實現的,找不到一個確定的返回父Loader的方式,於是就返回null。
5,類的載入類載入有三種方式:1、命令列啟動應用時候由JVM初始化載入2、通過Class.forName()方法動態載入3、通過ClassLoader.loadClass()方法動態載入 三種方式區別比較大,看個例子就明白了:public class HelloWorld {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader loader = HelloWorld.class.getClassLoader();
System.out.println(loader);
//使用ClassLoader.loadClass()來載入類,不會執行初始化塊
loader.loadClass("Test2");
//使用Class.forName()來載入類,預設會執行初始化塊
// Class.forName("Test2");
//使用Class.forName()來載入類,並指定ClassLoader,初始化時不執行靜態塊
// Class.forName("Test2", false, loader);
}
} public class Test2 {
static {
System.out.println("靜態初始化塊執行了!");
}
} 分別切換載入方式,會有不同的輸出結果。6,自訂ClassLoad為了說明問題,先看例子:package test;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class MyClassLoader {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException {
URL url = new URL("file:/E:\\projects\\testScanner\\out\\production\\testScanner");
ClassLoader myloader = new URLClassLoader(new URL[]{url});
Class c = myloader.loadClass("test.Test3");
System.out.println("----------");
Test3 t3 = (Test3) c.newInstance();
}
} public class Test3 {
static {
System.out.println("Test3的靜態初始化塊執行了!");
}
} 運行後:----------
Test3的靜態初始化塊執行了!
Process finished with exit code 0 可以看出自訂了ClassLoader myloader = new URLClassLoader(new URL[]{url});已經成功將類Test3載入到記憶體了,並通過預設構造方法構造了對象Test3 t3 = (Test3) c.newInstance(); 有關ClassLoader還有很重要一點:同一個ClassLoader載入的類檔案,只有一個Class執行個體。但是,如果同一個類檔案被不同的ClassLoader載入,則會有兩份不同的ClassLoader執行個體(前提是著兩個類載入器不能用相同的父類載入器)。
java類載入過程,瞭解一下?