一次編譯,到處運行
java一直宣傳的口號是:一次編譯,到處運行。那么它如何實現(xiàn)的呢?我們看下圖:
graph TD java原程序--javac編譯-->java字節(jié)碼 java字節(jié)碼-->jvm虛擬機
jvm虛擬機--java解釋-->windows機器碼 jvm虛擬機--java解釋-->linux機器碼 windows機器碼-->windows執(zhí)行
linux機器碼-->linux執(zhí)行
java程序經(jīng)過一次編譯之后,將java代碼編譯為字節(jié)碼也就是class文件,然后在不同的操作系統(tǒng)上依靠不同的java虛擬機
進行解釋,最后再轉(zhuǎn)換為不同平臺的機器碼,最終得到執(zhí)行。這樣我們是不是可以推演,如果要在mac系統(tǒng)上運行,是不是只需要安裝mac java虛擬機
就行了。那么了解了這個基本原理后,我們來看一下,一段程序是如何執(zhí)行的。
public class HelloWorld { public static void main(String[] args) {
System.out.print("Hello world"); } }
這段程序從編譯到運行,所經(jīng)歷的過程如下:
graph TD java源代碼--class文件-->java字節(jié)碼 java字節(jié)碼--加載jvm.cfg文件-->加載配置
加載配置--根據(jù)jvm.cfg的配置-->加載jvm.dll文件 加載jvm.dll文件-->初始化jvm 初始化jvm-->獲取JNI接口
獲取JNI接口--JNI為本地方法他可以直接與操作系統(tǒng)交互-->操作Class文件 操作Class文件-->找到main文件執(zhí)行
jvm基本結(jié)構(gòu)
可能通過上面的描述,大家對JVM運行流程有了一個粗略的認(rèn)識,那么JVM內(nèi)部到底是怎么執(zhí)行一個class文件的呢?
graph TD Class文件-->類加載器 類加載器-->內(nèi)存空間 內(nèi)存空間--運行時常量池-->方法區(qū) 方法區(qū)-->垃圾回收GC
內(nèi)存空間--對象存儲-->java堆 java堆-->垃圾回收GC 內(nèi)存空間--局部變量表_棧幀_操作數(shù)-->java棧
java棧--線程結(jié)束自動釋放-->線程私有 內(nèi)存空間--本地方法庫_C語言-->本地方法棧 本地方法棧-->線程私有 內(nèi)存空間--JNI直接操作-->堆外內(nèi)存
jvm內(nèi)存分類介紹
JVM內(nèi)存空間包含:方法區(qū)、java堆、java棧、本地方法棧。
*
方法區(qū)是各個線程共享的區(qū)域,存放類信息、常量、靜態(tài)變量。
*
java堆也是線程共享
的區(qū)域,我們的類的實例就放在這個區(qū)域,可以想象你的一個系統(tǒng)會產(chǎn)生很多實例,因此java堆的空間也是最大的。如果java堆空間不足了,程序會拋出OutOfMemoryError異常。
*
java棧是每個線程私有的區(qū)域,它的生命周期與線程相同,一個線程對應(yīng)一個java棧,每執(zhí)行一個方法就會往棧中壓入一個元素,這個元素叫“棧幀”,而棧幀
中包括了方法中的局部變量、用于存放中間狀態(tài)值的操作棧,如果java??臻g不足了,程序會拋出StackOverflowError異常.
每個幀代表一個方法,Java方法有兩種返回方式,return和拋出異常,兩種方式都會導(dǎo)致該方法對應(yīng)的幀出棧和釋放內(nèi)存。
*
本地方法棧角色和java棧類似,只不過它是用來表示執(zhí)行本地方法的,本地方法棧存放的方法調(diào)用本地方法接口,最終調(diào)用本地方法庫,實現(xiàn)與操作系統(tǒng)、硬件交互的目的。
*
PC寄存器(程序計數(shù)器),說到這里我們的類已經(jīng)加載了,實例對象、方法、靜態(tài)變量都去了自己改去的地方,那么問題來了,程序該怎么執(zhí)行,哪個方法先執(zhí)行,哪個方法后執(zhí)行,這些指令執(zhí)行的順序就是PC寄存器在管,它的作用就是控制程序指令的執(zhí)行順序。
執(zhí)行引擎當(dāng)然就是根據(jù)PC寄存器調(diào)配的指令順序,依次執(zhí)行程序指令。
* 靜態(tài)變量+常量+類信息+運行時常量池存在方法區(qū)中,實例變量存在堆內(nèi)存中。
* 基本類型的變量和對象的引用變量都是在函數(shù)的棧內(nèi)存中分
熱門工具 換一換