#### **類的加載過程**
類從被加載到內存開始,會經歷加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、和卸載(Unloading)這7個階段,其中驗證、準備和解析3個統稱為連接(Linking)。

圖1 類的生命周期
其中加載、驗證、準備、初始化和卸載這5個接口的順序是確定的,但是解析階段是不一定的,因為java語言的動態綁定。
**加載(Loading)** :
加載階段虛擬機要完成3件事情:
1. 通過一個類的全限定名來獲取定義此類的二進制流。
2. 將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構
3. 在內存中生成一個代表這個類的Class對象,作為方法區這個類的各種數據的訪問入口。
**類的加載時機**
Java虛擬機規范中并沒有強制約束什么時候開始進行加載。但是對于初始化階段,虛擬機規范嚴格規定了“**有且只有5種情況必須立即對類進行初始化**”,而加載、驗證、準備肯定是在初始化之前要做的,那其實也間接的約束加載,也可以理解為類的加載時機。
1. 遇到new、getstatic、pubstatic或invokestatic這4條字節碼指定時,如果類沒有進行初始化,需要先初始化。這4個指令對應Java代碼的場景分別是:使用new關鍵字創建對象時、讀取或者設置一個類的靜態自動(不被final修飾的)、調用一個類的靜態方法時。
2. 使用java.lang.reflect包中的方法對類進行反射調用時,如果沒有初始化先初始化。
3. 當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化。
4. 當虛擬機啟動時,用戶指定一個要執行的主類,就是程序的入口,帶main方法的類,那么要先初始化這個主類。
5. 當使用JDK1.7的java.lang.invoke.MethodHandle實例最后的解析結果為REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這和對應的類有沒初始化,要先觸發初始化。
上邊5種叫做主動引用,除了這5種情況其他引用類的情況不會被初始化,也叫做被動引用。
**驗證(Verification)**:
驗證是連接階段的第一步,這一階段的目的為了確保Class文件字節流中包含的信息符合虛擬機的要求。驗證階段大致會從一下4個方面驗證。
1. **文件格式的驗證**:驗證字節流是否符合Class文件的格式規范,例如是否是以魔數0xCAFEBABE開頭的、版本號是否在虛擬機的處理范圍內、常量池中是否有不支持的常量類型、CONSTANT_Uff8_info類型的常量中是否有不和UTF8編碼的、Class文件中各個部分及文件本身是否有刪除或者附加的信息等。
2. **元數據驗證**:對字節碼進行分析,看是否符合Java的語言規范要求。例如是否所有的類都繼承自Object、這個類是否繼承了被final修飾的類、如果是抽象類是否實現了父類的方法等。
3. **字節碼驗證**:通過分析字節碼判斷代碼是合法的、符合邏輯的。例如保證跳轉指針不會跳轉到方法體外的指令上、保證方法體重的類型轉換是有效的等。一個類的方法字節碼沒有通過字節碼驗證肯定是有問題的,但是一個方法字節碼通過了驗證,也不能說他一定是安全的。
4. 符號引用驗證:這一步是為了解析做準備,判斷符號引用通過全限定名是否能找到對應的類、符號引用中的類,字段,方法的訪問修飾符是否可以被當前類方法,在這一步如果如果不能通過符號引用可能會拋出如下異常:
java.lang.IllegalAccessError
java.lang.NoSuchFieldError
java.lang.NoSuchMethodError
**準備** :
準備階段是給static修飾的變量分配內存并設置初始值的過程。這里說了是被static修飾的變量,類中的成員變量是在對象實例化的時候隨著對象分配在Java堆中的。
這里設置初始值有兩種情況,如果這個static的變量沒有被final修飾的話那么設置的就是這個類型的默認值,直到類執行到初始化階段才會按照代碼中設定的值初始化。
如果是被final修飾的static變量會在這里階段被設置成代碼中的值,想想為什么
**解析** :
解析階段是虛擬機將常量池內的符號引用替換為直接引用的過程,這里包括對類或者接口的解析、字段解析、類方法解析、接口方法解析。
**初始化** :
到了初始化才真正開始執行Java代碼或者說是字節碼,在準備階段變量被設置了一次初始值,在這個階段則需要根據代碼中設定的值初始化static變量和其他的資源,也可以理解為初始化階段是執行構造器< clinit >()方法的過程。
**< clinit >()方法**:是由編譯器自動生成的,它用來執行類中所有的static變量的賦值工作和靜態語句塊中的代碼。執行的順序是由Java代碼的出現順序決定的,靜態語句塊只能訪問定義在靜態語句塊之前的變量,定義在它之后的變量,它只可以進行賦值,但不能訪問。
#### **Java中的類加載**

圖2 類加載
* Bootstrap ClassLoader
負責加載$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++實現,不是ClassLoader子類
* Extension ClassLoader
負責加載java平臺中擴展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目錄下的jar包
* App ClassLoader
負責記載classpath中指定的jar包及目錄中class
* Custom ClassLoader
屬于應用程序根據自身需要**自定義的ClassLoader**,如tomcat、jboss都會根據j2ee規范自行實現ClassLoader。
加載過程中會先檢查類是否被已加載,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個classloader已加載就視為已加載此類,保證此類只所有ClassLoader加載一次。而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。
#### **Dex 和動態加載類機制**
**背景**:
在Android 應用開發的一般情況下,常規的開發方式和代碼架構就能滿足普通的需求。但是有些特殊問題,常常引發進一步的思考。例如應該怎么樣開發一個可以自定義控件的Android應用?就像Eclipse 一樣,可以**動態加載插件**。如何**讓Android 應用執行服務器上的不可預知的代碼**?如何**對Android 應用加密,而只在執行時白解密,從而防止被破解**?上述問題,我們**可以使用類加載器來靈活的加載執行的類**。
**類加載機制**
Dalvik 虛擬機如同其他Java 虛擬機一樣,在運行程序時首先需要將對應的類加載到內存中。而在標準的Java 虛擬機中,類加載可以從class 文件中讀取,也可以是其他形式的二進制流。因此,我們常常利用這一點,在**程序運行時手動力日載Class ,從而達到代碼動態加載執行的目的。**
然而Dalvik 虛擬機畢竟不算是標準的Java 虛擬機,因此在類加載機制上,它們有相同的地方,也有不同的地方。我們必須區別對待。例如當在使用標準Java 虛擬機時,我們經常自定義繼承自ClassLoader 的類加載器。然后通過defineClass 方法來從一個二進制流中加載Class 。然而,這在Android 里是行不通的,大家就沒必要走彎路了。參看源碼我們知道, Android 中ClassLoader 的defineClass 方法具體是調用VMC lassLoader 的defineClass 本地靜態方法。而這個本地方法除了拋出一個“ UnsupportedOperationException ”之外,什么都沒做,甚至連返回值都為空。
**Dalvik 虛擬機類加載機制**
那如果在Dalvik 虛擬機里, ClassLoader 不好使,我們如何實現動態加載類呢?Android 為我們從ClassLoader 派生出了兩個類: DexClassLoader 和PathClassLoader。
- 前言
- Android 熱補丁技術——資源的熱修復
- 插件化系列詳解
- Dex分包——MultiDex
- Google官網——配置方法數超過 64K 的應用
- IMOOC熱修復與插件化筆記
- 第1章 class文件與dex文件解析
- Class文件解析
- dex文件解析
- class與dex對比
- 第2章 虛擬機深入講解
- 第3章 ClassLoader原理講解
- 類的加載過程
- ClassLoade源碼分析
- Android中的動態加載
- 第4章 熱修復簡單講解
- 第5章 熱修復AndFix詳解
- 第6章 熱修復Tinker詳解及兩種方式接入
- 第7章 引入熱修復后代碼及版本管理
- 第8章 插件化原理深入講解
- 第9章 使用Small完成插件化
- 第10章 使用Atlas完成插件化
- 第11章 課程整體總結
- DN學院熱修復插件化筆錄
- 插件化
- 熱修復
- Android APP開發應掌握的底層知識
- 概述
- Binder
- AIDL
- AMS
- Activity的啟動和通信原理
- App啟動流程第2篇
- App內部的頁面跳轉
- Context家族史
- Service
- BroadcastReceiver
- ContentProvider
- PMS及App安裝過程