天天看點

淺析Java記憶體模型--ClassLoader

在了解ClassLoader之前,我們先回顧下Java的一些相關知識。

基礎

1. Java的主要特性

  • 平台無關性
  • 面向對象
  • GC(Java的垃圾回收機制)
  • 類庫
  • 語言特性
  • 異常處理

2. 為什麼JVM直接将 源碼解析成機器碼去執行

  • 準備工作:每次執行都需要各種檢查
  • 相容性:也可以将别的語言解析成位元組碼

Compile Once,Run Anywhere如何實作

淺析Java記憶體模型--ClassLoader

​ java源碼首先被編譯成位元組碼,再由不同平台的JVM進行解析,Java語言在不同的平台上運作時不需要進行重新編譯,Java虛拟機在執行位元組碼的時候,把位元組碼 轉換成具體平台上的機器指令。

3. JVM如何加載.class檔案

淺析Java記憶體模型--ClassLoader
  • Class Loader:依據特定格式,加載class檔案到記憶體
  • Execution Engine:對指令 進行 解析
  • Native Interface:融合不同開發語言的原生庫為 Java所用
  • Runtime Data Area:JVM記憶體空間結構模型

4. 反射

​ Java反射機制是在運作狀态中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意 一個對象,都能夠調用它的任意方法和屬性;這種動态擷取資訊以及動态調用對象方法的功能稱為 Java語言的反射機制。

5. 類從編譯到執行的過程

  • 編譯器将

    Xxx.java

    源檔案編譯為

    Xxx.class

    位元組碼檔案
  • ClassLoader 将位元組碼轉換為JVM中的

    Class<Xxx>

    對象
  • JVM利用

    Class<Xxx>

    對象執行個體化為

    Xxx

    對象

談談ClassLoader

​ ClassLoader 在 Java 中有着非常重要的作用,它主要工作在 Class 裝載的加載階段,其主要作用是從系統外部獲得 Class 二進制資料流。它是 Java 的核心元件,所有的 Class 都是由 ClassLoader 進行加載的,ClassLoader 負責通過将 Class 檔案裡的二進制資料流裝載進系統,然後交給 Java 虛拟機進行連接配接、初始化等操作。

1. ClassLoader的種類

  • BootStrapClassLoader:C++ 編寫,加載核心庫 java.*
  • ExcClassLoader:Java 編寫,加載擴充庫 javax.*
  • AppClassLoader:Java 編寫,加載程式所在目錄
  • 自定義 ClassLoader:Java 編寫,定制化加載

2. 自定義 ClassLoader 的實作

關鍵函數:

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw  new ClassNotFoundException(name);
}

protected final Class<?> defineClass(byte[] b, int off, int len) 
    throws ClassFormatError {
    
    return defineClass(null, b, off, len, null);
}
           

3. 類的加載方式

  • 隐式加載:new
  • 顯示加載:loadClass,forName等

4. loadClass和forName的差別

類的裝載過程

淺析Java記憶體模型--ClassLoader

5. loadClass和forName的差別

  • Class.forName得到的class是已經初始化完成的
  • Classloader.loadClass得到的class是還沒有連結的

6. 談談類加載器的雙親委派機制

不同類的加載方式和加載路徑不同,為了實作分工,各自實作各自的功能,使得邏輯更加的明确,才有這麼多共存的ClassLoader,加載類會根據各自的區域各司其職,而雙親委派機制會使這些加載器互相協調,形成一個整體。

雙親委派機制的原理圖:

淺析Java記憶體模型--ClassLoader

loadClass源碼解析:

protected Class<?> loadClass(String name, boolean resolve) 
    throws ClassNotFoundException
{
	synchronized (getClassLoadingLock(name)) {
		// 首先,檢測該類是否已被加載
		Class<?> c = findLoadedClass(name);
        if (c == null) {
        	long t0 = System.nanoTime();
            try {
            	// 檢查上層是否已被加載
            	if (parent != null) {
            		c = parent.loadClass(name, false);
                } else {
                	c = findBootstrapClassOrNull(name);    
                }
            } catch (ClassNotFoundException e) {
				// 如果未發現類,則抛出異常
			}
			
            if (c == null) {
            	// 如果仍未找到,則委托findClass方法去尋找
            	long t1 = System.nanoTime();
            	// 自定義的findClass方法
            	c = findClass(name);
            	
            	sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
            	sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
            	sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
        	resolveClass(c);
        }
        return c;
	}
}
           

為什麼要使用雙親委派機制去加載類?

為了避免多份同樣位元組碼的加載。(通過逐層檢查可以避免多份由各自加載的相同 class檔案 )