JVM 類的加載

                                                                    

  1. 什麼是類的加載?

         將類通過javac編譯的.class文件中的二進制數據結構轉化爲方法區的運行時數據結構,再在堆中生成該類對應的java.lang.Class對象,然後就可以通過該對象訪問方法區中的這些數據。

 

      2.類的生命週期

      類的生命週期包括加載、連接(驗證、準備、解析)、初始化、使用和卸載。

  • 加載,查找並加載類的二進制數據,在Java堆中也創建一個java.lang.Class類的對象
  • 連接,連接又包含三塊內容:驗證、準備、解析

          1)驗證,文件格式、元數據、字節碼、符號引用驗證;

           2)準備,爲類的靜態變量分配內存,並將其初始化爲默認的零值(0、0L、null、false等);

           3)解析,把類中的符號引用轉換爲直接引用

  • 初始化,執行類的構造器,爲類的靜態變量賦予正確的初始值     

<clinit>,類構造器方法,在jvm第一次加載class文件時調用,因爲是類級別的,所以只加載一次,是編譯器自動收集類中所有類變量(static修飾的變量)和靜態語句塊(static{}),中的語句合併產生的,編譯器收集的順序,是由程序員在寫在源文件中的代碼的順序決定的。

<init>,實例構造器方法,在實例創建出來的時候調用,包括調用new操作符;調用Class或java.lang.reflect.Constructor對象的newInstance()方法;調用任何現有對象的clone()方法;通過java.io.ObjectInputStream類的getObject()方法反序列化。

 

        初始化,爲類的靜態變量賦予正確的初始值,JVM負責對類進行初始化,主要對類變量進行初始化。在Java中對類變量進             行 初始值設定有兩種方式: ①聲明類變量是指定初始值    ②使用靜態代碼塊爲類變量指定初始值

 

 

  • 使用,new出對象程序中使用
  • 卸載,執行垃圾回收

 

 

      3.類加載器

public class ClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        System.out.println(loader);
        System.out.println(loader.getParent());
        System.out.println(loader.getParent().getParent());
                
    }
}

//結果
/*sun.misc.Launcher$AppClassLoader@18b4aac2
  sun.misc.Launcher$ExtClassLoader@3cd1a2f1
  null    啓動類加載器:它使用C++實現 ExtClassLoader找不到
*/

  

 

啓動類加載器: BootstrapClassLoader,負責加載存放在 JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被 -Xbootclasspath參數指定的路徑中的,並且能被虛擬機識別的類庫(如rt.jar,所有的java.開頭的類均被 BootstrapClassLoader加載)。啓動類加載器是無法被Java程序直接引用的。

擴展類加載器: ExtensionClassLoader,該加載器由 sun.misc.Launcher$ExtClassLoader實現,它負責加載 JDK\jre\lib\ext目錄中,或者由 java.ext.dirs系統變量指定的路徑中的所有類庫(如javax.開頭的類),開發者可以直接使用擴展類加載器。

應用程序類加載器: ApplicationClassLoader,該類加載器由 sun.misc.Launcher$AppClassLoader來實現,它負責加載用戶類路徑(ClassPath)所指定的類,開發者可以直接使用該類加載器,如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。

總的來說分爲兩類加載器,Bootstrap ClassLoader虛擬機自身的一部分,其它加載器獨立於虛擬機外部的加載器並且全部繼承自java.lang.ClassLoader

 

 

       4.雙親委派模型

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            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) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

雙親委派模型:除了頂層的啓動類加載器外,其餘的類加載器都應該有自己的父類加載器,而這種父子關係一般通過組合(Composition)關係來實現,而不是通過繼承(Inheritance)。特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。JVM中兩個類是否“相等”,首先就必須是同一個類加載器加載的,所以雙親委派模型保證了類的一致性。

     5.緩存機制

          緩存機制將會保證所有加載過的Class都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存區尋找該Class,只有緩存區不存在,系統纔會讀取該類對應的二進制數據,並將其轉換成Class對象,存入緩存區。這就是爲什麼修改了Class後,必須重啓JVM,程序的修改纔會生效

 

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