new一個對象發生了什麼之類加載機制

類加載

在這裏插入圖片描述
類加載的過程包括了加載、驗證、準備、解析、初始化五個階段。在這五個階段中,加載、驗證、準備和初始化這四個階段發生的順序是確定的,而解析階段則不一定,它在某些情況下可以在初始化階段之後開始。另外注意這裏的幾個階段是按順序開始,而不是按順序進行或完成,因爲這些階段通常都是互相交叉地混合進行的,通常在一個階段執行的過程中調用或激活另一個階段。

  • 加載
    ”加載“是”類加機制”的第一個過程,在加載階段,虛擬機主要完成三件事:
    (1)通過一個類的全限定名來獲取其定義的二進制字節流
    (2)將這個字節流所代表的的靜態存儲結構轉化爲方法區的運行時數據結構
    (3)在堆中生成一個代表這個類的Class對象,作爲方法區中這些數據的訪問入口。
    相對於類加載的其他階段而言,加載階段是可控性最強的階段,因爲程序員可以使用系統的類加載器加載,還可以使用自己的類加載器加載。
  • 驗證
    驗證的主要作用就是確保被加載的類的正確性。也是連接階段的第一步。說白了也就是我們加載好的.class文件不能對我們的虛擬機有危害。
  • 準備
    準備階段主要爲類變量分配內存並設置初始值。這些內存都在方法區分配。在這個階段我們只需要注意兩點就好了,也就是類變量和初始值兩個關鍵詞:
    (1)類變量(static)會分配內存,但是實例變量不會,實例變量主要隨着對象的實例化一塊分配到java堆中,
    (2)這裏的初始值指的是數據類型默認值,而不是代碼中被顯示賦予的值。比如
    public static int value = 1; //在這裏準備階段過後的value值爲0,而不是1。賦值爲1的動作在初始化階段。
  • 解析
    虛擬機將常量池內的符號引用替換爲直接引用的過程。解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定符 7 類符號引用進行。
  • 初始化
    到初始化階段,才真正開始執行類中定義的 Java 程序代碼,此階段是執行 () 方法的過程。
    () 方法是由編譯器按語句在源文件中出現的順序,依次自動收集類中的所有類變量的賦值動作和靜態代碼塊中的語句合併產生的。(不包括構造器中的語句。構造器是初始化對象的,類加載完成後,創建對象時候將調用的 () 方法來初始化對象)

雙親委派模型

如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到頂層的啓動類加載器中,只有當父加載器反饋自己無法完成這個加載請求(它的搜索範圍中沒有找到所需的類)時,子加載器纔會嘗試自己去加載。這種父子關係並不是通過繼承實現,而是通過組合。
在這裏插入圖片描述
體現雙親委派模型的代碼

// 代碼摘自《深入理解Java虛擬機》
    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 首先,檢查請求的類是否已經被加載過了
        Class c = findLoadedClass(name);
        if (c == null) {
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
            // 如果父類加載器拋出ClassNotFoundException
            // 說明父類加載器無法完成加載請求
            }
            if (c == null) {
                // 在父類加載器無法加載的時候
                // 再調用本身的findClass方法來進行類加載
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }

破壞雙親委派模型的

  • 在1.2之前的代碼,都是繼承ClassLoader的唯一目的就是重寫loadClass。並沒有引入雙親委派模型。

  • 基礎類無法調用類加載器加載用戶提供的代碼
    雙親委派很好地解決了各個類加載器的基礎類的統一問題(越基礎的類由越上層的加載器進行加載),但如果基礎類又要調用用戶的代碼,例如 JNDI 服務,JNDI 現在已經是 Java 的標準服務,它的代碼由啓動類加載器去加載(在 JDK 1.3 時放進去的 rt.jar ),但 JNDI 的目的就是對資源進行集中管理和查找,它需要調用由獨立廠商實現並部署在應用程序的 ClassPath 下的 JNDI 接口提供者(SPI,Service Provider Interface,例如 JDBC 驅動就是由 MySQL 等接口提供者提供的)的代碼,但啓動類加載器只能加載基礎類,無法加載用戶類。通過ThreadContextClassLoader可以線程綁定一個類加載器,不綁定默認是ApplicationClassLoader,這樣子就有辦法獲取加載器去加載用戶代碼了。

new一個對象的過程

1.確認要new的對象的類信息是否加載了。若未加載,執行上述加載。

2.分配對象的內存。計算對象佔用空間,並分配內存。

3.設定默認值,成員變量需要設定默認值,這裏指的是數據類型默認值。

4.設置對象頭,設置新對象的哈希嗎、GC信息、鎖信息等。

5.執行init方法,初始化成員變量(代碼值),調用類的構造函數。

參考

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