原文連接java
類的加載過程
加載
- 經過全類名獲取類的二進制字節流
- 將字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構
- 在內存中生成一個表明這個類的
java.lang.Class
對象,做爲方法區這個類的各類數據的方法入口
從何處獲取字節流:c++
- 從本地磁盤獲取
- 從網絡獲取
- 運行時計算生成(動態代理)
- 從其餘文件生成(JSP文件)
- 從加密文件中獲取
連接
驗證
目的:確保Class文件的字節流中包含的信息符合《JAVA虛擬機的》所有約束要求,保證這些信息被當作代碼運行後不會危害虛擬機自身的安全web
驗證的四個階段
文件格式驗證安全
- 目的是保證輸入的字節流可以正確的解析並存儲於方法區以內,格式上符合描述一個Java類型信息的要求
元數據驗證網絡
- 目的是對類的元數據信息進行語義校驗,保證不存在與《Java虛擬機》定義相悖的元數據信息
- 字節碼驗證
目的是經過數據流分析和控制分析,肯定程序語義是合法的且符合邏輯數據結構
符號引用驗證jvm
準備
- 爲類變量分配內存並設置變量初始值的階段
基本類型的初始值加密
數據類型 |
零值 |
int |
0 |
long |
0L |
short |
(short)0 |
char |
''\u0000' |
byte |
(byte)0 |
boolean |
false |
float |
0.0f |
double |
0.0d |
reference |
null |
解析
目的:Java虛擬機將常量池內的符號引用替換爲直接引用spa
解析範圍:代理
- 類和接口的解析
- 字段解析
- 方法解析
- 接口方法解析
初始化
初始化類變量和其餘資源。初始化階段就是執行類構造器<clinit>()
方法的過程。<clinit>()
是Java編譯器的自動化產物。
<clinit>()
是由編譯器自動收集類中的全部類變量的賦值動做和靜態語句塊中的語句合併產生的,編譯器收集的順序是由在源文件中出現的順序決定的
- 在子類
<clinit>()
方法執行前,父類的<clinit>()
方法會執行完畢
- 若是一個類或接口中沒有靜態語句塊或變量賦值操做,編譯器不會爲這個類生成
<clinit>()
方法
類加載器
引導類加載器用於加載Java核心類庫。例如:String.class和HashMap.class
引導類加載器
- 經過C或C++語言實現,嵌套在JVM內部
- 不繼承
java.lang.ClassLoader
,沒有父加載器
- 加載擴展類加載器和應用類加載器
自定義類加載器
- 使用Java語言編寫
- 存在包含關係
啓動類加載器(Bootstrap ClassLoader)
- 使用c/c++語言實現,嵌套在JVM內部
- 用來加載核心類庫
- 不繼承子
java.lang.ClassLoader
, 沒有父加載器
- 加載擴展類和應用程序類加載器,並制定爲他們的父類加載器
- Bootstrap啓動類加載器只加載包名爲java、java、sun等開頭的類
擴展類加載器(ExtClassLoader)
- Java語言編寫
- 派生於ClassLoader類
- 父類加載器爲Bootstrap ClassLoader
系統類加載器(AppClassLoader)
- Java語言編寫
- 派生於ClassLoader類
- 父類爲ExtClassLoader
- 負責加載環境變量classpath或系統屬性java.class.path指定路徑下的類
- 是默認的類加載器
如何實現本身的類加載器?
- 實現
ClassLoader
類,並重寫findClass()
方法
爲何要自定義類加載器?
- 隔離加載類
- 修改類加載的方式
- 擴展加載源
- 防止源碼泄露
雙親委派機制
原理:
- 若是一個類加載器收到一個類加載請求,它不會本身先去加載,而是把這個請求委託給父類加載器去執行
- 若是父類加載器還存在父類加載器,則進一步向上委託,依次遞歸,請求最終將到達頂層的Bootstrap ClassLoader
- 若是父類加載器能夠完成類加載任務,就成功返回,若父類加載器沒法完成加載任務,子加載器纔會嘗試本身去加載
優點
- 避免類的重複加載
- 保護程序安全,防止核心API被隨意篡改
其餘
在JVM中表示兩個class對象是否爲同一個類存在的必要條件