文章目錄
1. 內存結構概述
注:類文件通過類的加載系統(加載==>鏈接==>初始化)加載到內存中(方法區,堆),執行引擎執行字節碼指令(程序計數器、虛擬機棧、本地方法棧、堆區、方法區配合)
2. 類的加載過程
2.1 類加載器的作用
- 類加載器子系統負責從文件系統或者網絡中加載class文件,class文件頭有特定的文件表示
- ClassLoader只負責class文件的加載,至於它是否可以運行,則有ExecutionEngine決定
- 加載的類信息存放於一塊稱爲方法區的內存空間。除了類的信息外,方法區還會存放運行時常量池信息,可能還包括字符串字面量和數字常量(這部分常量信息是Class文件中常量池部分的內存映射)
2.2 類加載器角色
- class file存在於本地硬盤上,可以理解爲設計師畫在紙上的模板,而最終這個模板在執行的時候是要加載到JVM當中來,根據這個文件實例化出n個一模一樣的實例
- class file 加載到jvm中,被稱爲DNA元數據模板,放在方法區。
- 在.class文件->JVM->最終成爲元數據模板,此過程就要一個運輸工具,將class送到內存中充當模板,這就是類加載器充當的角色
2.3 類的加載過程
1. 加載
- 通過一個類的全限定名獲取此定義類的二進制字節流
- 將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構
- 在內存中生成一個代表這個類的java.lang.Class對象,作爲方法區這個類的各種數據的訪問入口
2. 鏈接
- 驗證
- 目的在於確保Class文件的字節流中包含信息符合當前虛擬機要求,保證被加載類的正確性,不會危害虛擬機自身安全
- 主要包括四種驗證,文件格式驗證,元數據驗證,字節碼驗證,符號引用驗證
- 準備
- 爲類變量分配內存並且設置該類變量的默認初始值,既零值
- 這裏不包含用final修飾的static,因爲final在編譯的時候就分配了,準備階段會顯示初始化
- 這裏不會爲實例變量初始化,類變量會分配在方法區,而實例變量是會隨着對象一起分配到java堆中
- 解析
- 將常量池內的符號引用轉換爲直接引用的過程
- 事實上,解析操作往往會伴隨着JVM在執行完之後再執行
- 符號引用就是一組符號來描述所引用的目標,符號引用的字面量形式明確定義在《java虛擬機規範》的Class文件格式中。直接引用就是指向目標的指針,相對偏移量或一個間接定位到目標的句柄
- 解析動作主要針對類或接口,字段,類方法,接口方法,方法類型等,對應的常量池中的CONSTANT_CLASS_INFO,CONSTANT_Fieleref_info,CONSTANT_Methodref_info等
3. 初始化
- 初始化階段就是執行類構造器方法()的過程
- 此方法不需要定義,是javac編譯器自動收集類中的所有類變量的賦值動作和靜態代碼塊的語句合併而來
- 構造器方法中指令按語句在源文件中出現的順序執行
- ()不同於類的構造器,構造器在虛擬機視角下是()
- 若該類具有父類,JVM會保證
()執行前,父類的
()執行完畢
-虛擬機必須保證一個類的
()方法在多線程下被同步加鎖
3. 類加載器的分類
3.1 引導類加載器(Bootstrap ClassLoader)
- c/c++語言編寫
- 加載核心java核心庫
JAVA_HOME/jre/lib/rt.jar
resources.jar
sun.boot.class.path
- 不繼承ClassLoader,沒有父類加載器
- 加載擴展類加載器和應用程序加載器,並指定爲他們的父類加載器
- 只加載java,javax,sun開頭的包中的類
3.2 自定義類加載器(User-Defined ClassLoader)
3.2.1 擴展類加載器(Extention ClassLoader)
- java語言實現,sun.misc.Launcher.ExtClassLoader實現
- 派生於ClassLoader類
- 父類加載器,爲啓動類加載器
- 從java.ext.dirs系統所指定的目錄加載類庫,或從jdk按安裝目錄的jre/lib/ext子目錄下加載類庫,用戶的jar包放在這個目錄也會加載
3.2.2 應用程序類加載器(Application ClassLoader)
- java語言編寫,由sun.misc.Launcher.AppClassLoader實現
- 派生與ClassLoader類
- 父類加載器爲擴展類加載器
- 負責加載classpath或系統屬性java.class.path指定路徑下的類庫
- 該類加載器是程序默認的加載器,一般來說,java應用的類都是由它加載完成
- ClassLoader.getSystemClassLoader();可以獲取到該類加載器
3.2.3 自定義加載器
- 作用
1. 隔離加載類
2. 修改類的加載方式
3. 擴展加載源
4. 防止源碼泄露
4. ClassLoader的使用說明
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("1:" + systemClassLoader);
ClassLoader classLoader = LogUtilTest.class.getClassLoader();
System.out.println("2:" + classLoader);
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
System.out.println("3:" + contextClassLoader);
5. 雙親委派機制
5.1 什麼時候加載類
按需加載,當需要使用該類的時候,class文件被加載,採用雙親委派模式,交給父類加載器加載
5.2 工作原理
- 一個類加載器收到了類加載請求,它並不會自己先去加載,而是把這個請求委託給父類的加載器去執行
- 如果父類加載器還存在其父類加載器,則進一步向上委託,依次遞歸請求最終將到達頂層的啓動類加載器
- 如果父類加載器可以完成類加載任務,就成功返回,倘若父類加載器無法完成此加載任務,子加載器纔會嘗試自己去加載,這就是雙親委派模式。
5.3 雙親委派模型的破壞
- 雙親委派的加載機制是寫在loadClass()中的,在jdk1.2版本以前,自定義類加載器重寫了這個方法,破壞了雙親委派機制
- JNDI,JDBC等由java提供接口,由不同的廠商提供實現,接口類由啓動類加載器去加載,接口實現類由線程上下文類加載器加載(默認就是應用程序加載器),父類委派了子類的加載器
- OSGI
5.4 雙親委派機制的優勢
- 避免類重複被加載
- 保證程序安全,防止核心API被破壞(自定義java.lang.String不能被加載-沙箱安全機制)
6. 其它
判斷兩個Class是否爲同一個類的條件
- 類的完整類名必須一致,包括包名
- 加載這個類的ClassLoader必須一致