類加載子系統
- 類加載器子系統負責從文件系統或者網絡中加載Class文件,class文件在文件開頭有特定的文件標識。
- ClassLoader
只負責
class文件的加載
,至於它是否可以運行,則由Execution Engine
(執行引擎)決定。 - 加載的類信息存放於一塊稱爲方法區的內存空間。除了類的信息外,方法區中還會存放運行時常最池信息,可能還包括字符串字而量和數字常量(這部分常量信息是Class文件中常量池部分的內存映射)
例子
- class file存在於本地硬盤上,可以理解爲設計師畫在紙上的模板,而最終這個模板在執行的時候是要加載到JVM當中來根據這個文件實例畫出n個- -模-樣的實例。
- class file加載到JVM中,被稱爲DNA元數據模板,放在
方法區
。
3.在.class文件-> JVM ->最終成爲元數據模板,此過程就要-一個運輸工具(類裝載器Class Loader)
,扮演-一個快遞員的角色。
比如我們有一個HellowLoader類,他的類轉載過程如下圖所示。
類加載過程
加載-Loading
1.通過一個類的全限定名
獲取定義此類的二進制字節流
(以物理磁盤爲例)
2.將這個字節流所代表的靜態存儲結構轉化爲方法區
的運行時數據結構
3.在內存中生成一個代表這個類的java. lang.Class
對象,作爲方法區這個類的各種數據的訪問入口
補充:加載. class文件的方式
1.從本地系統中直接加載
2.通過網絡獲取,|典型場景: web Applet
3.從zip壓縮包中讀取,成爲日後jar、war格式的基礎
4.運行時計算生成,使用最多的是:動態代理技術
5.由其他文件生成,典型場景: JSP應用
6.從專有數據庫中提取. class文件,比較少見
7.從加密文件中獲取,典型的防Class文件被反編譯的保護措施
鏈接-Linking
驗證(Verify)
- 目的在於
確保Class文件的字節流中包含信息符合當前虛擬機要求
,保證被加載類的正確性
,不會危害虛擬機自身安全。(虛擬機要求:例如字節碼文件以cafebabe開頭等) - 主要包括
四種驗證
,文件格式驗證,元數據驗證,字節碼驗證,符號引用驗證。(驗證出錯會報VerifyError錯誤)
準備(Prepare)
- 爲類變量分配內存並且設置該類變量的默認初始值。
- 這裏
不包含用final修飾的static,
因爲final在編譯的時候就會分配了,準備階段會顯式初始化;
- 這裏不會爲
實例變量分配初始化
,類變量會在方法區中,而實例變量是會隨着對象一起分配到Java堆中
。
解析(Resolve)
- 將
常量池內
的符號引用轉換爲直接引用的過程。 - 事實上,
解析操作
往往會伴隨着JVM在執行完*初始化*之後再執行
。 - 符號引用就是一組符號來描述所引用的目標。符號引用的字面量形式明確定義在《java虛擬機規範》的class文件格式中。直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄。
解析動作主要針對類或接口、字段、類方法、接口方法、方法類型等
。對應常量池中的CONSTANT_ Class_ info、 CONSTANT_ Fieldref_ info、 CONSTANT Methodref_ info等。
初始化-Initialization
- 初始化階段就是執行
類構造器方法<clinit> ()
的過程。(注意:這個方法和類中定義的構造方法不一樣) - 此方法不需定義,
是javac編譯器
自動收集類中的所有類變量
的賦值動作和靜態代碼塊
中的語句(也就是static修飾的)合併而來
。Clinit構造器會把顯示初始化和構造代碼塊初始化合並在一起構成構造器方法,如果沒有類變量(靜態變量)的賦值動作或者是今天代碼塊語句那麼就不會生成這個clinit方法了。還有一點:<init> ()
這個就是默認的構造方法。
- 構造器方法中指令按語句在源文件中出現的
順序執行
。這裏要注意我在前面有一篇文章類成員加載順序說到了類成員加載順序是:父類的靜態字段——>父類靜態代碼塊——>子類靜態字段——>子類靜態代碼塊——>父類成員變量(非靜態字段)——>父類非靜態代碼塊——>父類(無參)構造器——>子類成員變量——>子類非靜態代碼塊——>子類構造器,這裏要注意的就是靜態字段和靜態代碼塊之間對類(靜態)變量的初始化,是按照書寫順序的,並不是按照先字段初始化再代碼塊初始化
。
- ()
不同於類的構造器
。(關聯: 構造器是虛擬機視角下的 ())
- 若該類具有父類,JVM會保證子類的()執行前,父類的()已經執行完畢。
- 虛擬機必須保證一個類的()方法在
多線程下被同步加鎖
。