類加載的流程 加載->連接->初始化->使用->卸載 連接過程: 驗證->準備->解析
第一個階段:加載
什麼情況下必須進行加載呢?
使用關鍵字 new getStatic putStatic invokestatic 這四個字節碼指令的時候 |
對類型進行反射調用的時候 |
初始化一個類,但是發現還沒有初始化父類的時候 |
虛擬機啓動時,主類啓動的時候 |
java動態語言支持,解析結果爲獲取類型的靜態參數的時候 |
JDK8中 default定義的接口方法的實現類初始化的時候,必須先觸發接口方法的初始化 |
加載過程虛擬機執行的動作
1.根據類的全限定名稱,獲取類的二進制字節流
2.將類型的靜態存儲結構放入到方法區中(所以靜態屬性是最早進入方法區的)
3.在內存中生成該類型的Class對象,作爲該類型的訪問入口
第二個階段:驗證
文件格式驗證:對於Class文件的規定的格式的驗證 |
元數據驗證:類型的類關係驗證,比如是否有父類,父類的繼承時候符合要求等語義校驗 |
字節碼驗證:保證字節碼能按照順序進行工作,不會因爲意外跳轉到別的類型字節碼指令上去,保證字節碼的合法性 |
符號引用驗證:類型的可訪問行。類型屬性的可訪問行等,確保解析行爲能執行 |
第三階段:準備
爲類中定義的變量(靜態變量)分配內存,並設置類變量的初始值,這部分內存會在java8之後放在堆內存中而不是方法區。
第四階段:解析
將常量池內的符號引用替換爲直接引用的過程。
符號引用:使用一組符號描述引用的對象
直接引用:直接指向目標的指針,或者能間接定位到目標的句柄。
第五階段:初始化
JVM 執行類中編寫的java代碼
類加載器:
通過一個類型的全限定名去獲取類型的二進制字節流 這個動作是放在JVM之外執行的,讓程序自己決定如何去加載這個類。
一個類型的唯一性是由類加載器和類本身來確定的,也就是說 類是同一個類,但是類加載器不同的話,運行程序判斷這兩個類型依舊不屬於同一個類型。
雙親委派模型:
類加載器的分類:
啓動類加載器 |
擴展類加載器 |
應用程序類加載器 |
自定義類加載器 |
這些類加載器的層級依次降低
雙親委派模型:除了頂層啓動類加載器之外,其他的加載器都必須有自己的父類加載器。爲什麼又叫“雙親委派”呢,因爲類加載器之間的關係不是父子關係,是組合關係,組合起來就是被加載類的雙親。(你就想象這個類的父親娶了一個跟你差不多年齡的後媽就行了,但是你還得叫她 媽)
工作流程:如果一個類加載器收到了一個類加載的請求,首先他自己不會去加載這個類,會讓它的父類加載器去加載這個類型,每一層都是這麼來搞得,因此最後加載的請求就會傳到最頂層的啓動類加載器,只有當父類加載器反饋說我不能加載這個類的時候,子類加載器才進行加載。
優點:Java的類會隨着類加載器的加載過程具備一種帶優先級的層級關係。也就是說一個類型在JVM中肯定能確保是同一個類,(如果這裏有疑問可以看上面類加載器的介紹,如何確定唯一的一個類型)