玩轉Java虛擬機(四)

打卡學習JVM,第四天

本人學習過程中所整理的代碼,源碼地址

- 類的卸載

  • 當一個類被加載、連接和初始化後,它的生命週期就開始了。都這個類的Class對象不再被引用時,即不可觸及時,Class對象就會結束生命週期,此類在方法區內的數據也會被卸載,從而結束該類的生命週期
  • 一個類何時結束生命週期,取決於代表它的Class對象合適結束生命週期
  • 由用戶自定義的類加載器所加載的類是可以被卸載的,Java虛擬機自帶的類加載器加載的類是不能被卸載的,Java虛擬機本身會始終引用這些類加載器,而這些類加載器則會始終引用它們所加載的類的Class對象,因此這些Class對象始終是可觸及的

- 命名空間在類加載過程的作用

public class MyCat {

    public MyCat() {
        System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());

        System.out.println("from MyCat:" + MySample.class);
    }
}
public class MySample {

    public MySample() {
        System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());

        new MyCat();
    }
}
public class MyTest17 {
    public static void main(String[] args) throws Exception {
        CustomClassLoader loader1 = new CustomClassLoader("loader1");

        Class<?> clazz = loader1.loadClass("classloader.MySample");
        System.out.println("class:" + clazz.hashCode());
        //如果註釋掉該行,那麼並不會實例化MySample對象,即MySample構造方法不會被調用,因此不會實例化MyCat對象,即沒有對MyCat進行主動使用,這裏就不會加載MyCat Class
        Object object = clazz.newInstance();
    }
}

首先我們在classpath路徑下刪除MySample.class文件,程序運行結果沒有拋出異常,具體的加載順序如下:

  • 加載MySample類->委託給系統類加載器->classpath中找不到MySample.class文件->CustomClassLoader加載MySample類->加載成功
  • 加載MyCat類->CustomClassLoader加載->委託給系統類加載器加載->加載成功

如果刪除MyCat.class文件,程序運行會拋出異常,那麼造成這種結果的原因是什麼呢?具體看下加載順序就會明白了:

  • 加載MySample類->委託給系統類加載器->加載成功
  • 加載MyCat類->系統類加載器加載->classpath中找不到MyCat.class文件->拋出異常

總結:主要原因在於加載MyCat類的加載器是和MySample的類加載器是一樣的,從這裏我們又可以衍生出關於命名空間的一個重要結論,

- 關於命名空間的重要說明

  • 子加載器所加載的類能夠訪問到父加載器所加載的類
  • 父加載器所加載的類無法訪問到子加載器所加載的類

- 如何查看加載器的具體路徑信息

public class MyTest18 {
    public static void main(String[] args) { 
    	//	根加載器
        System.out.println(System.getProperty("sun.boot.class.path"));
        //擴展類加載器
        System.out.println(System.getProperty("java.ext.dirs"));
        //系統類加載器
        System.out.println(System.getProperty("java.class.path"));
    }
}

- 類加載器的雙親委託模型的好處

  1. 可以確保Java核心庫的類型安全:所有的Java應用都至少會引用java.lang.Object類,也就是說在運行期,java.lang.Object類會被加載到Java虛擬機中;如果這個加載過程是由Java應用自己的類加載器所完成的,那麼很可能在JVM中存在多個版本的java.lang.Object類,而且這些類之間還是不兼容的,相互不可見的(命名空間在發揮着作用)。藉助於雙親委託機制,Java核心類庫中的類的加載工作都是由啓動類加載器來統一完成的,從而確保了Java應用所使用的都是同一個版本的Java核心類庫,它們之間是互相兼容的
  2. 可以確保Java核心類庫所提供的類不會被自定義的類所替代
  3. 不同的類加載器可以爲相同名稱(binary name)的類創建額外的命名空間,相同名稱的類可以並存在Java虛擬機中,子還需要用不同的類加載器來加載他們即可,不同類加載器所加載的類之間是不兼容的,這就相當於在Java虛擬機內部創建了一個又一個相互隔離的Java類空間,這類技術在很多框架中都得到了實際應用

在運行期,一個Java類是由該類的完全限定名(binary name)和用於加載該類的定義類加載器所共同決定的。如果同樣名字(完全限定名相同)的類是由兩個不同的加載器所加載,那麼這些類就是不同的,即便.class文件的字節碼完全一樣,並且從相同的位置加載亦如此

在Oracle的Hotspot實現中,系統熟悉sun.boot.class.path如果修改錯了,則運行會出錯,提示如下錯誤信息

Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object

關於啓動類加載器的一些重要知識:在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章