一.JVM類加載機制
首先需要了解一下類加載器(ClassLoader):
ClassLoader是Java的一個核心組件,它主要作用是從系統外部獲得Class二進制數據流,然后將數據流裝載到系統,交給JVM進行連接,初始化等操作,所有的Class都是有ClassLoader進行加載的。
點開ClassLoader類,如下圖所示:
類加載器種類:
啟動類加載器(BootStrapClassLoader):它用來加載 Java 的核心類,如java.xxx。
擴展類加載器(ExtensionsClassLoader):它負責加載JRE的擴展目錄,如javax.xxx。
應用類加載器(AppClassLoader):它一般用來加載程序所在目錄下的類。
自定義類加載器:通過繼承ClassLoader類實現自定義類加載器。
雙親委派機制:
虛擬機是根據類的全限定名來加載類的,那么有個問題,如果同時存在兩個或多個全限定名完全一致的情況下,該如何選擇加載哪個類?如何判斷該類是否被加載過了?這都是雙親委派機制能做的。
下圖是雙親委派機制的實現(這是ClassLoader中的loadClass方法),很明顯,parent也是一個類加載器。
雙親委派機制原理:
1.一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,直到啟動類加載器
2.啟動類加載器檢查能不能加載(使用findClass()方法),能就加載(結束);否則,拋出異常,通知子加載器進行加載。
雙親委派機制的優點是:
附:Java跨平台實現的原理:Java源碼首先被編譯成字節碼(不同平台編譯后的字節碼是一樣的,因此在不同平台上運行時不需要重新編譯),再由不同平台的JVM進行解析,將字節碼轉換成具體平台上的機器指令。
類加載流程:
加載:通過ClassLoader加載class文件字節碼,生成Class對象
連接:檢查加載的class文件的正確性和安全性;為類變量分配存儲空間並設置類變量初始值;JVM將常量池內的符號引用轉換為直接引用
初始化:執行類變量賦值和靜態代碼塊
類加載的方式:
隱式加載:通過new關鍵字來創建實例對象。
顯式加載:
通過Class.forName()來加載類,它得到的是已經初始化完成的class
通過類加載器的loadClass()方法來加載類,它得到的class還沒有完成連接過程
二.JVM內存模型
所有線程共享的內存數據區:方法區,堆。而虛擬機棧,本地方法棧和程序計數器都是線程私有的。
1、存放於棧中的:
每個線程包含一個棧區,棧中只保存基礎數據類型的對象和自定義對象的引用(如變量的名字)。
每個棧中的數據(基礎數據類型和對象引用)都是私有的,其他棧不能訪問。
方法的形式參數,方法調用完后從棧空間回收。
引用對象的地址,引用完后,棧空間地址立即被回收,堆空間等待GC。
2、存放於堆中的:
存儲的全部是對象,每個對象包含一個與之對應的class信息
Jvm只有一個堆區(heap)被所有線程共享,堆區中不存放基本類型和對象引用,只存放對象本身
3、存放於方法區中的:
存放線程所執行的字節碼指令
跟堆一樣.被所有線程共享.方法區包含:static變量
注意:Class實例在jdk1.6及之前是存放在方法區中, 在jdk1.7及之后存放在堆中
常量池在jdk1.6及之前是存放在方法區中,在jdk1.7存放在堆中,在jdk1.8存放在元空間中