精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Java代碼是如何被CPU狂飆起來的?

開發(fā) 前端
在介紹Java如何一步步被執(zhí)行起來之前,我們需要先弄明白為什么Java可以實現(xiàn)跨平臺運行,因為搞清楚了這個問題之后,對于我們理解Java程序如何被CPU執(zhí)行起來非常有幫助。

無論是剛剛?cè)腴TJava的新手還是已經(jīng)工作了的老司機(jī),恐怕都不容易把Java代碼如何一步步被CPU執(zhí)行起來這個問題完全講清楚。但是對于一個Java程序員來說寫了那么久的代碼,我們總要搞清楚自己寫的Java代碼到底是怎么運行起來的。另外在求職面試的時候這個問題也常常會聊到,面試官主要想通過它考察求職同學(xué)對于Java以及計算機(jī)基礎(chǔ)技術(shù)體系的理解程度,看似簡單的問題實際上囊括了JVM運行原理、操作系統(tǒng)以及CPU運行原理等多方面的技術(shù)知識點。我們一起來看看Java代碼到底是怎么被運行起來的。通過這種雙親委派模型,可以保證同一個類在不同的類加載器中只會被加載一次,從而避免了類的重復(fù)加載,也保證了類的唯一性。同時,由于每個類加載器只會加載自己所負(fù)責(zé)的類,因此可以防止惡意代碼的注入和類的篡改,提高了Java程序的安全性。

Java如何實現(xiàn)跨平臺

在介紹Java如何一步步被執(zhí)行起來之前,我們需要先弄明白為什么Java可以實現(xiàn)跨平臺運行,因為搞清楚了這個問題之后,對于我們理解Java程序如何被CPU執(zhí)行起來非常有幫助。

為什么需要JVM

write once run anywhere曾經(jīng)是Java響徹編程語言圈的slogan,也就是所謂的程序員開發(fā)完java應(yīng)用程序后,可以在不需要做任何調(diào)整的情況下,無差別的在任何支持Java的平臺上運行,并獲得相同的運行結(jié)果從而實現(xiàn)跨平臺運行,那么Java到底是如何做到這一點的呢?

其實對于大多數(shù)的編程語言來說,都需要將程序轉(zhuǎn)換為機(jī)器語言才能最終被CPU執(zhí)行起來。因為無論是如Java這種高級語言還是像匯編這種低級語言實際上都是給人看的,但是計算機(jī)無法直接進(jìn)行識別運行。因此想要CPU執(zhí)行程序就必須要進(jìn)行語言轉(zhuǎn)換,將程序語言轉(zhuǎn)化為CPU可以識別的機(jī)器語言。

圖片

學(xué)過計算機(jī)組成原理的同學(xué)肯定都知道,CPU內(nèi)部都是用大規(guī)模晶體管組合而成的,而晶體管只有高電位以及低點位兩種狀態(tài),正好對應(yīng)二進(jìn)制的0和1,因此機(jī)器碼實際就是由0和1組成的二進(jìn)制編碼集合,它可以被CPU直接識別和執(zhí)行。

圖片

但是像X86架構(gòu)或者ARM架構(gòu),不同類型的平臺對應(yīng)的機(jī)器語言是不一樣的,這里的機(jī)器語言指的是用二進(jìn)制表示的計算機(jī)可以直接識別和執(zhí)行的指令集集合。不同平臺使用的CPU不同,那么對應(yīng)的指令集也就有所差異,比如說X86使用的是CISC復(fù)雜指令集而ARM使用的是RISC精簡指令集。所以Java要想實現(xiàn)跨平臺運行就必須要屏蔽不同架構(gòu)下的計算機(jī)底層細(xì)節(jié)差異。因此,如何解決不同平臺下機(jī)器語言的適配問題是Java實現(xiàn)一次編寫,到處運行的關(guān)鍵所在。

那么Java到底是如何解決這個問題的呢?怎么才能讓CPU可以看懂程序員寫的Java代碼呢?其實這就像在我們的日常生活中,如果雙方語言不通,要想進(jìn)行交流的話就必須中間得有一個翻譯,這樣通過翻譯的語言轉(zhuǎn)換就可以實現(xiàn)雙方暢通無阻的交流了。打個比方,一個中國廚師要教法國廚師和阿拉伯廚師做菜,中國廚師不懂法語和阿拉伯語,法國廚師和阿拉伯廚師不懂中文,要想順利把菜做好就需要有翻譯來幫忙。中國廚師把做菜的菜譜告訴翻譯者,翻譯者將中文菜譜轉(zhuǎn)換為法文菜譜以及阿拉伯語菜譜,這樣法國廚師和阿拉伯廚師就知道怎么做菜了。

圖片

因此Java的設(shè)計者借助了這樣的思想,通過JVM(Java Virtual Machine,Java虛擬機(jī))這個中間翻譯來實現(xiàn)語言轉(zhuǎn)換。程序員編寫以.java為結(jié)尾的程序之后通過javac編譯器把.java為結(jié)尾的程序文件編譯成.class結(jié)尾的字節(jié)碼文件,這個字節(jié)碼文件需要JVM這個中間翻譯進(jìn)行識別解析,它由一組如下圖這樣的16進(jìn)制數(shù)組成。JVM將字節(jié)碼文件轉(zhuǎn)化為匯編語言后再由硬件解析為機(jī)器語言最終最終交給CPU執(zhí)行。

圖片

所以說通過JVM實現(xiàn)了計算機(jī)底層細(xì)節(jié)的屏蔽,因此windows平臺有windows平臺的JVM,Linux平臺有Linux平臺的JVM,這樣在不同平臺上存在對應(yīng)的JVM充當(dāng)中間翻譯的作用。因此只要編譯一次,不同平臺的JVM都可以將對應(yīng)的字節(jié)碼文件進(jìn)行解析后運行,從而實現(xiàn)在不同平臺下運行的效果。

圖片

那么問題又來了,JVM是怎么解析運行.class文件的呢?要想搞清楚這個問題,我們得先看看JVM的內(nèi)存結(jié)構(gòu)到底是怎樣的,了解JVM結(jié)構(gòu)之后這個問題就迎刃而解了。

JVM結(jié)構(gòu)

JVM(Java Virtual Machine)即Java虛擬機(jī),它的核心作用主要有兩個,一個是運行Java應(yīng)用程序,另一個是管理Java應(yīng)用程序的內(nèi)存。它主要由三部分組成,類加載器、運行時數(shù)據(jù)區(qū)以及字節(jié)碼執(zhí)行引擎。

圖片

類加載器

類加載器負(fù)責(zé)將字節(jié)碼文件加載到內(nèi)存中,主要經(jīng)歷加載-》連接-》實例化三個階段完成類加載操作。

圖片

另外需要注意的是.class并不是一次性全部加載到內(nèi)存中,而是在Java應(yīng)用程序需要的時候才會加載。也就是說當(dāng)JVM請求一個類進(jìn)行加載的時候,類加載器就會嘗試查找定位這個類,當(dāng)查找對應(yīng)的類之后將他的完全限定類定義加載到運行時數(shù)據(jù)區(qū)中。

運行時數(shù)據(jù)區(qū)

JVM定義了在Java程序運行期間需要使用到的內(nèi)存區(qū)域,簡單來說這塊內(nèi)存區(qū)域存放了字節(jié)碼信息以及程序執(zhí)行過程數(shù)據(jù)。運行時數(shù)據(jù)區(qū)主要劃分了堆、程序計數(shù)器虛擬機(jī)棧、本地方法棧以及元空間數(shù)據(jù)區(qū)。其中堆數(shù)據(jù)區(qū)域在JVM啟動后便會進(jìn)行分配,而虛擬機(jī)棧、程序計數(shù)器本地方法棧都是在常見線程后進(jìn)行分配。

圖片

不過需要說明的是在JDK 1.8及以后的版本中,方法區(qū)被移除了,取而代之的是元空間(Metaspace)。元空間與方法區(qū)的作用相似,都是存儲類的結(jié)構(gòu)信息,包括類的定義、方法的定義、字段的定義以及字節(jié)碼指令。不同的是,元空間不再是JVM內(nèi)存的一部分,而是通過本地內(nèi)存(Native Memory)來實現(xiàn)的。在JVM啟動時,元空間的大小由MaxMetaspaceSize參數(shù)指定,JVM在運行時會自動調(diào)整元空間的大小,以適應(yīng)不同的程序需求。

字節(jié)碼執(zhí)行引擎

字節(jié)碼執(zhí)行引擎最核心的作用就是將字節(jié)碼文件解釋為可執(zhí)行程序,主要包含了解釋器、即使編譯以及垃圾回收器。字節(jié)碼執(zhí)行引擎從元空間獲取字節(jié)碼指令進(jìn)行執(zhí)行。當(dāng)Java程序調(diào)用一個方法時,JVM會根據(jù)方法的描述符和方法所在的類在元空間中查找對應(yīng)的字節(jié)碼指令。字節(jié)碼執(zhí)行引擎從元空間獲取字節(jié)碼指令,然后執(zhí)行這些指令。

JVM如何運行Java程序

在搞清楚了JVM的結(jié)構(gòu)之后,接下來我們一起來看看天天寫的Java代碼是如何被CPU飆起來的。一般公司的研發(fā)流程都是產(chǎn)品經(jīng)理提需求然后程序員來實現(xiàn)。所以當(dāng)產(chǎn)品經(jīng)理把需求提過來之后,程序員就需要分析需求進(jìn)行設(shè)計然后編碼實現(xiàn),比如我們通過Idea來完成編碼工作,這個時候工程中就會有一堆的以.java結(jié)尾的Java代碼文件,實際上就是程序員將產(chǎn)品需求轉(zhuǎn)化為對應(yīng)的Java程序。但是這個.java結(jié)尾的Java代碼文件是給程序員看的,計算機(jī)無法識別,所以需要進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換為計算機(jī)可以識別的機(jī)器語言。

圖片

通過上文我們知道,Java為了實現(xiàn)write once,run anywhere的宏偉目標(biāo)設(shè)計了JVM來充當(dāng)轉(zhuǎn)換翻譯的工作。因此我們編寫好的.java文件需要通過javac編譯成.class文件,這個class文件就是傳說中的字節(jié)碼文件,而字節(jié)碼文件就是JVM的輸入。

圖片

當(dāng)我們有了.class文件也就是字節(jié)碼文件之后,就需要啟動一個JVM實例來進(jìn)一步加載解析.class字節(jié)碼。實際上JVM本質(zhì)其實就是操作系統(tǒng)中的一個進(jìn)程,因此要想通過JVM加載解析.class文件,必須先啟動一個JVM進(jìn)程。JVM進(jìn)程啟動之后通過類加載器加載.class文件,將字節(jié)碼加載到JVM對應(yīng)的內(nèi)存空間。

圖片

當(dāng).class文件對應(yīng)的字節(jié)碼信息被加載到中之后,操作系統(tǒng)會調(diào)度CPU資源來按照對應(yīng)的指令執(zhí)行java程序。

圖片

以上是CPU執(zhí)行Java代碼的大致步驟,看到這里我相信很多同學(xué)都有疑問這個執(zhí)行步驟也太大致了吧。哈哈,別著急,有了基本的解析流程之后我們再對其中的細(xì)節(jié)進(jìn)行分析,首先我們就需要弄清楚JVM是如何加載編譯后的.class文件的。

字節(jié)碼文件結(jié)構(gòu)

要想搞清楚JVM如何加載解析字節(jié)碼文件,我們就先得弄明白字節(jié)碼文件的格式,因為任何文件的解析都是根據(jù)該文件的格式來進(jìn)行。就像CPU有自己的指令集一樣,JVM也有自己一套指令集也就是Java字節(jié)碼,從根上來說Java字節(jié)碼是機(jī)器語言的.class文件表現(xiàn)形式。字節(jié)碼文件結(jié)構(gòu)是一組以 8 位為最小單元的十六進(jìn)制數(shù)據(jù)流,具體的結(jié)構(gòu)如下圖所示,主要包含了魔數(shù)、class文件版本、常量池、訪問標(biāo)志、索引、字段表集合、方法表集合以及屬性表集合描述數(shù)據(jù)信息。

圖片

這里簡單說明下各個部分的作用,后面會有專門的文章再詳細(xì)進(jìn)行闡述。

魔數(shù)與文件版本

魔數(shù)的作用就是告訴JVM自己是一個字節(jié)碼文件,你JVM快來加載我吧,對于Java字節(jié)碼文件來說,其魔數(shù)為0xCAFEBABE,現(xiàn)在知道為什么Java的標(biāo)志是咖啡了吧。而緊隨魔數(shù)之后的兩個字節(jié)是文件版本號,Java的版本號通常是以52.0的形式表示,其中高16位表示主版本號,低16位表示次版本號。。

常量池

在常量池中說明常量個數(shù)以及具體的常量信息,常量池中主要存放了字面量以及符號引用這兩類常量數(shù)據(jù),所謂字面量就是代碼中聲明為final的常量值,而符號引用主要為類和接口的完全限定名、字段的名稱和描述符以及方法的名稱以及描述符。這些信息在加載到JVM之后在運行期間將符號引用轉(zhuǎn)化為直接引用才能被真正使用。常量池的第一個元素是常量池大小,占據(jù)兩個字節(jié)。常量池表的索引從1開始,而不是從0開始,這是因為常量池的第0個位置是用于特殊用途的。

圖片

訪問標(biāo)志

類或者接口的訪問標(biāo)記,說明類是public還是abstract,用于描述該類的訪問級別和屬性。訪問標(biāo)志的取值范圍是一個16位的二進(jìn)制數(shù)。

索引

包含了類索引、父類索引、接口索引數(shù)據(jù),主要說明類的繼承關(guān)系。

字段表集合

主要是類級變量而不是方法內(nèi)部的局部變量。

方法表集合

主要用來描述類中有幾個方法,每個方法的具體信息,包含了方法訪問標(biāo)識、方法名稱索引、方法描述符索引、屬性計數(shù)器、屬性表等信息,總之就是描述方法的基礎(chǔ)信息。

屬性表集合

方法表集合之后是屬性表集合,用于描述該類的所有屬性。屬性表集合包含了所有該類的屬性的描述信息,包括屬性名稱、屬性類型、屬性值等等。

解析字節(jié)碼文件

知道了字節(jié)碼文件的結(jié)構(gòu)之后,JVM就需要對字節(jié)碼文件進(jìn)行解析,將字節(jié)碼結(jié)構(gòu)解析為JVM內(nèi)部流轉(zhuǎn)的數(shù)據(jù)結(jié)構(gòu)。大致的過程如下:

1、讀取字節(jié)碼文件

JVM首先需要讀取字節(jié)碼文件的二進(jìn)制數(shù)據(jù),這通常是通過文件輸入流來完成的。

2、解析字節(jié)碼

JVM解析字節(jié)碼的過程是將字節(jié)碼文件中的二進(jìn)制數(shù)據(jù)解析為Java虛擬機(jī)中的數(shù)據(jù)結(jié)構(gòu)。首先JVM首先會讀取字節(jié)碼文件的前四個字節(jié),判斷魔數(shù)是否為0xCAFEBABE,以此來確認(rèn)該文件是否是一個有效的Java字節(jié)碼文件。JVM接著會解析常量池表,將其中的常量轉(zhuǎn)換為Java虛擬機(jī)中的數(shù)據(jù)結(jié)構(gòu),例如將字符串常量轉(zhuǎn)換為Java字符串對象。解析類、接口、字段、方法等信息:JVM會依次解析類索引、父類索引、接口索引集合、字段表集合、方法表集合等信息,將這些信息轉(zhuǎn)換為Java虛擬機(jī)中的數(shù)據(jù)結(jié)構(gòu)。最后,JVM將解析得到的數(shù)據(jù)結(jié)構(gòu)組裝成一個Java類的結(jié)構(gòu),并將其放入元空間中。

在完成字節(jié)碼文件解析之后,接下來就需要類加載器閃亮登場了,類加載器會將類文件加載到JVM內(nèi)存中,并為該類生成一個Class對象。

類加載

加載器啟動

我們都知道,Java應(yīng)用的類都是通過類加載器加載到運行時數(shù)據(jù)區(qū)的,這里很多同學(xué)可能會有疑問,那么類加載器本身又是被誰加載的呢?這有點像先有雞還是先有蛋的靈魂拷問。實際上類加載器啟動大致會經(jīng)歷如下幾個階段:

圖片

1、以linux系統(tǒng)為例,當(dāng)我們通過"java"啟動一個Java應(yīng)用的時候,其實就是啟動了一個JVM進(jìn)程實例,此時操作系統(tǒng)會為這個JVM進(jìn)程實例分配CPU、內(nèi)存等系統(tǒng)資源;

2、"java"可執(zhí)行文件此時就會解析相關(guān)的啟動參數(shù),主要包括了查找jre路徑、各種包的路徑以及虛擬機(jī)參數(shù)等,進(jìn)而獲取定位libjvm.so位置,通過libjvm.so來啟動JVM進(jìn)程實例;

3、當(dāng)JVM啟動后會創(chuàng)建引導(dǎo)類加載器Bootsrap ClassLoader,這個ClassLoader是C++語言實現(xiàn)的,它是最基礎(chǔ)的類加載器,沒有父類加載器。通過它加載Java應(yīng)用運行時所需要的基礎(chǔ)類,主要包括JAVA_HOME/jre/lib下的rt.jar等基礎(chǔ)jar包;

4、而在rt.jar中包含了Launcher類,當(dāng)Launcher類被加載之后,就會觸發(fā)創(chuàng)建Launcher靜態(tài)實例對象,而Launcher類的構(gòu)造函數(shù)中,完成了對于ExtClassLoader及AppClassLoader的創(chuàng)建。Launcher類的部分代碼如下所示:

public class Launcher {
private static URLStreamHandlerFactory factory = new Factory();
//類靜態(tài)實例
private static Launcher launcher = new Launcher();
private static String bootClassPath = System.getProperty("sun.boot.class.path");
private ClassLoader loader;
private static URLStreamHandler fileHandler;

public static Launcher getLauncher() {
return launcher;
}
//Launcher構(gòu)造器
public Launcher() {
ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}

try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}

Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
} catch (InstantiationException var6) {
} catch (ClassNotFoundException var7) {
} catch (ClassCastException var8) {
}
} else {
var3 = new SecurityManager();
}

if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}

System.setSecurityManager(var3);
}

}
...
}

雙親委派模型

為了保證Java程序的安全性和穩(wěn)定性,JVM設(shè)計了雙親委派模型類加載機(jī)制。在雙親委派模型中,啟動類加載器(Bootstrap ClassLoader)、擴(kuò)展類加載器(Extension ClassLoader)以及應(yīng)用程序類加載器(Application ClassLoader)按照一個父子關(guān)系形成了一個層次結(jié)構(gòu),其中啟動類加載器位于最頂層,應(yīng)用程序類加載器位于最底層。當(dāng)一個類加載器需要加載一個類時,它首先會委派給它的父類加載器去嘗試加載這個類。如果父類加載器能夠成功加載這個類,那么就直接返回這個類的Class對象,如果父類加載器無法加載這個類,那么就會交給子類加載器去嘗試加載這個類。這個過程會一直持續(xù)到頂層的啟動類加載器。

圖片

通過這種雙親委派模型,可以保證同一個類在不同的類加載器中只會被加載一次,從而避免了類的重復(fù)加載,也保證了類的唯一性。同時,由于每個類加載器只會加載自己所負(fù)責(zé)的類,因此可以防止惡意代碼的注入和類的篡改,提高了Java程序的安全性。

數(shù)據(jù)流轉(zhuǎn)過程

當(dāng)類加載器完成字節(jié)碼數(shù)據(jù)加載任務(wù)之后,JVM劃分了專門的內(nèi)存區(qū)域內(nèi)承載這些字節(jié)碼數(shù)據(jù)以及運行時中間數(shù)據(jù)。其中程序計數(shù)器、虛擬機(jī)棧以及本地方法棧屬于線程私有的,堆以及元數(shù)據(jù)區(qū)屬于共享數(shù)據(jù)區(qū),不同的線程共享這兩部分內(nèi)存數(shù)據(jù)。我們還是以下面這段代碼來說明程序運行的時候,各部分?jǐn)?shù)據(jù)在Runtime data area中是如何流轉(zhuǎn)的。

public class Test {
public static void main(String[] args) {
User user = new User();
Integer result = calculate(user.getAge());
System.out.println(result);
}

private static Integer calculate(Integer age) {
Integer data = age + 3;
return data;
}

}
以上代碼對應(yīng)的字節(jié)碼指令如下所示:

圖片

如上代碼所示,JVM創(chuàng)建線程來承載代碼的執(zhí)行過程,我們可以將線程理解為一個按照一定順序執(zhí)行的控制流。當(dāng)線程創(chuàng)建之后,同時創(chuàng)建該線程獨享的程序計數(shù)器(Program Counter Register)以及Java虛擬機(jī)棧(Java Virtual Machine Stack)。如果當(dāng)前虛擬機(jī)中的線程執(zhí)行的是Java方法,那么此時程序計數(shù)器中起初存儲的是方法的第一條指令,當(dāng)方法開始執(zhí)行之后,PC寄存器存儲的是下一個字節(jié)碼指令的地址。但是如果當(dāng)前虛擬機(jī)中的線程執(zhí)行的是naive方法,那么程序計數(shù)器中的值為undefined。

那么程序計數(shù)器中的值又是怎么被改變的呢?如果是正常進(jìn)行代碼執(zhí)行,那么當(dāng)線程執(zhí)行字節(jié)碼指令時,程序計數(shù)器會進(jìn)行自動加1指向下一條字節(jié)碼指令地址。但是如果遇到判斷分支、循環(huán)以及異常等不同的控制轉(zhuǎn)移語句,程序計數(shù)器會被置為目標(biāo)字節(jié)碼指令的地址。另外在多線程切換的時候,虛擬機(jī)會記錄當(dāng)前線程的程序計數(shù)器,當(dāng)線程切換回來的時候會根據(jù)此前記錄的值恢復(fù)到程序計數(shù)器中,來繼續(xù)執(zhí)行線程的后續(xù)的字節(jié)碼指令。

除了程序計數(shù)器之外,字節(jié)碼指令的執(zhí)行流轉(zhuǎn)還需要虛擬機(jī)棧的參與。我們先來看下虛擬機(jī)棧的大致結(jié)構(gòu),如下圖所示,棧大家肯定都知道,它是一個先入后出的數(shù)據(jù)結(jié)構(gòu),非常適合配合方法的執(zhí)行過程。虛擬機(jī)棧操作的基本元素就是棧幀,棧幀的結(jié)構(gòu)主要包含了局部變量、操作數(shù)棧、動態(tài)連接以及方法返回地址這幾個部分。

圖片



局部變量:主要存放了棧幀對應(yīng)方法的參數(shù)以及方法中定義的局部變量,實際上它是一個以0為起始索引的數(shù)組結(jié)構(gòu),可以通過索引來訪問局部變量表中的元素,還包括了基本類型以及對象引用等。非靜態(tài)方法中,第0個槽位默認(rèn)是用于存儲this指針,而其他參數(shù)和變量則會從第1個槽位開始存儲。在靜態(tài)方法中,第0個槽位可以用來存放方法的參數(shù)或者其他的數(shù)據(jù)。

操作數(shù)棧:和虛擬機(jī)棧一樣操作數(shù)棧也是一個棧數(shù)據(jù)結(jié)構(gòu),只不過兩者存儲的對象不一樣。操作數(shù)棧主要存儲了方法內(nèi)部操作數(shù)的值以及計算結(jié)果,操作數(shù)棧會將運算的參與方以及計算結(jié)果都壓入操作數(shù)棧中,后續(xù)的指令操作就可以從操作數(shù)棧中使用這些值來進(jìn)行計算。當(dāng)方法有返回值的時候,返回值也會被壓入操作數(shù)棧中,這樣方法調(diào)用者可以獲取到返回值。

動態(tài)鏈接:一個類中的方法可能會被程序中的其他多個類所共享使用,因此在編譯期間實際無法確定方法的實際位置到底在哪里,因此需要在運行時動態(tài)鏈接來確定方法對應(yīng)的地址。動態(tài)鏈接是通過在棧幀中維護(hù)一張方法調(diào)用的符號表來實現(xiàn)的。這張符號表中保存了當(dāng)前方法中所有調(diào)用的方法的符號引用,包括方法名、參數(shù)類型和返回值類型等信息。當(dāng)方法需要調(diào)用另一個方法時,它會在符號表中查找所需方法的符號引用,然后進(jìn)行動態(tài)鏈接,確定方法的具體內(nèi)存地址。這樣,就能夠正確地調(diào)用所需的方法。

方法返回地址:當(dāng)一個方法執(zhí)行完畢后,JVM會將記錄的方法返回地址數(shù)據(jù)置入程序計數(shù)器中,這樣字節(jié)碼執(zhí)行引擎可以根據(jù)程序計數(shù)器中的地址繼續(xù)向后執(zhí)行字節(jié)碼指令。同時JVM會將方法返回值壓入調(diào)用方的操作棧中以便于后續(xù)的指令計算,操作完成之后從虛擬機(jī)棧中獎棧幀進(jìn)行彈出。

知道了虛擬機(jī)棧的結(jié)構(gòu)之后,我們來看下方法執(zhí)行的流轉(zhuǎn)過程是怎樣的。

1、JVM啟動完成.class文件加載之后,它會創(chuàng)建一個名為"main"的線程,并且該線程會自動調(diào)用定義在該類中的名為"main"的靜態(tài)方法,這也是Java程序的入口點。

2、當(dāng)JVM在主線程中調(diào)用當(dāng)方法的時候就會創(chuàng)建當(dāng)前線程獨享的程序計數(shù)器以及虛擬機(jī)棧,在Test.class類中,開始執(zhí)行mian方法 ,因此JVM會虛擬機(jī)棧中壓入main方法對應(yīng)的幀棧幀。

圖片

3、在棧幀的操作數(shù)棧中存儲了操作的數(shù)據(jù),JVM執(zhí)行字節(jié)碼指令的時候從操作數(shù)棧中獲取數(shù)據(jù),執(zhí)行計算操作之后再將結(jié)果壓入操作數(shù)棧。

4、當(dāng)進(jìn)行calculate方法調(diào)用的時候,虛擬機(jī)棧繼續(xù)壓入calculate方法對應(yīng)的棧幀,被調(diào)用方法的參數(shù)、局部變量和操作數(shù)棧等信息會存儲在新創(chuàng)建的棧幀中。其中該棧幀中的方法返回地址中存放了main方法執(zhí)行的地址信息,方便在調(diào)用方法執(zhí)行完成后繼續(xù)恢復(fù)調(diào)用前的代碼執(zhí)行。

圖片

5、對于age + 3一條加法指令,在執(zhí)行該指令之前,JVM會將操作數(shù)棧頂部的兩個元素彈出,并將它們相加,然后將結(jié)果推入操作數(shù)棧中。在這個例子中,指令的操作碼是“add”,它表示執(zhí)行加法操作;操作數(shù)是0,它表示從操作數(shù)棧的頂部獲取第一個操作數(shù);操作數(shù)是1,它表示從操作數(shù)棧的次頂部獲取第二個操作數(shù)。

6、程序計數(shù)器中存儲了下一條需要執(zhí)行操作的字節(jié)碼指令的地址,因此Java線程執(zhí)行業(yè)務(wù)邏輯的時候必須借助于程序計數(shù)器才能獲得下一步命令的地址。

7、當(dāng)calculate方法執(zhí)行完成之后,對應(yīng)的棧幀將從虛擬機(jī)棧中彈出,其中方法執(zhí)行的結(jié)果會被壓入main方法對應(yīng)的棧幀中的操作數(shù)棧中,而方法返回地址被重置到main現(xiàn)場對應(yīng)的程序計數(shù)器中,以便于后續(xù)字節(jié)碼執(zhí)行引擎從程序計數(shù)器中獲取下一條命令的地址。如果方法沒有返回值,JVM仍然會將一個null值推送到調(diào)用該方法的棧幀的操作數(shù)棧中,作為占位符,以便恢復(fù)調(diào)用方的操作數(shù)棧狀態(tài)。

8、字節(jié)碼執(zhí)行引擎中的解釋器會從程序計數(shù)器中獲取下一個字節(jié)碼指令的地址,也就是從元空間中獲取對應(yīng)的字節(jié)碼指令,在獲取到指令之后,通過翻譯器翻譯為對應(yīng)的匯編語言而再交給硬件解析為機(jī)器指令,最終由CPU進(jìn)行執(zhí)行,而后再將執(zhí)行結(jié)果進(jìn)行寫回。

CPU執(zhí)行程序

通過上文我們知道無論什么編程語言最終都需要轉(zhuǎn)化為機(jī)器語言才能被CPU執(zhí)行,但是CPU、內(nèi)存這些硬件資源并不是直接可以和應(yīng)用程序打交道,而是通過操作系統(tǒng)來進(jìn)行統(tǒng)一管理的。對于CPU來說,操作系統(tǒng)通過調(diào)度器(Scheduler)來決定哪些進(jìn)程可以被CPU執(zhí)行,并為它們分配時間片。它會從就緒隊列中選擇一個進(jìn)程并將其分配給CPU執(zhí)行。當(dāng)一個進(jìn)程的時間片用完或者發(fā)生了I/O等事件時,CPU會被釋放,操作系統(tǒng)的調(diào)度器會重新選擇一個進(jìn)程并將其分配給CPU執(zhí)行。也就是說操作系統(tǒng)通過進(jìn)程調(diào)度算法來管理CPU的分配以及調(diào)度,進(jìn)程調(diào)度算法的目的就是為了最大化CPU使用率,避免出現(xiàn)任務(wù)分配不均空閑等待的情況。主要的進(jìn)程調(diào)度算法包括了FCFS、SJF、RR、MLFQ等。

CPU如何執(zhí)行指令?

前文中我們大致搞清楚了類是如何被加載的,各部分類字節(jié)碼數(shù)據(jù)在運行時數(shù)據(jù)區(qū)怎么流轉(zhuǎn)以及字節(jié)碼執(zhí)行引擎翻譯字節(jié)碼。實際上在運行時數(shù)據(jù)區(qū)數(shù)據(jù)流轉(zhuǎn)的過程中,CPU已經(jīng)參與其中了。程序的本質(zhì)是為了根據(jù)輸入獲得相應(yīng)的輸出,而CPU本質(zhì)就是根據(jù)程序的指令一步步執(zhí)行獲得結(jié)果的工具。對于CPU來說,它核心工作主要分為如下三個步驟;

1、獲取指令

CPU從PC寄存器中獲取對應(yīng)的指令地址,此處的指令地址是將要執(zhí)行指令的地址,根據(jù)指令地址獲取對應(yīng)的操作指令到指令寄存中,此時如果是順存執(zhí)行則PC寄存器地址會自動加1,但是如果程序涉及到條件、循環(huán)等分支執(zhí)行邏輯,那么PC寄存器的地址就會被修改為下一條指令執(zhí)行的地址。

2、指令譯碼

將獲取到的指令進(jìn)行翻譯,搞清楚哪些是操作碼哪些是操作數(shù)。CPU首先讀取指令中的操作碼然后根據(jù)操作碼來確定該指令的類型以及需要進(jìn)行的操作,CPU接著根據(jù)操作碼來確定指令所需的寄存器和內(nèi)存地址,并將它們提取出來。

3、執(zhí)行指令

經(jīng)過指令譯碼之后,CPU根據(jù)獲取到的指令進(jìn)行具體的執(zhí)行操作,并將指令運算的結(jié)果存儲回內(nèi)存或者寄存器中。

圖片

因此一旦CPU上電之后,它就像一個勤勞的小蜜蜂一樣,一直不斷重復(fù)著獲取指令-》指令譯碼-》執(zhí)行指令的循環(huán)操作。

CPU如何響應(yīng)中斷?

當(dāng)操作系統(tǒng)需要執(zhí)行某些操作時,它會發(fā)送一個中斷請求給CPU。CPU在接收到中斷請求后,會停止當(dāng)前的任務(wù),并轉(zhuǎn)而執(zhí)行中斷處理程序,這個處理程序是由操作系統(tǒng)提供的。中斷處理程序會根據(jù)中斷類型,執(zhí)行相應(yīng)的操作,并返回到原來的任務(wù)繼續(xù)執(zhí)行。

在執(zhí)行完中斷處理程序后,CPU會將之前保存的程序現(xiàn)場信息恢復(fù),然后繼續(xù)執(zhí)行被中斷的程序。這個過程叫做中斷返回(Interrupt Return,IRET)。在中斷返回過程中,CPU會將處理完的結(jié)果保存在寄存器中,然后從棧中彈出被中斷的程序的現(xiàn)場信息,恢復(fù)之前的現(xiàn)場狀態(tài),最后再次執(zhí)行被中斷的程序,繼續(xù)執(zhí)行之前被中斷的指令。

那么CPU又是如何響應(yīng)中斷的呢?主要經(jīng)歷了以下幾個步驟:

圖片

1、保存當(dāng)前程序狀態(tài)

CPU會將當(dāng)前程序的狀態(tài)(如程序計數(shù)器、寄存器、標(biāo)志位等)保存到內(nèi)存或棧中,以便在中斷處理程序執(zhí)行完畢后恢復(fù)現(xiàn)場。

2、確定中斷類型

CPU會檢查中斷信號的類型,以確定需要執(zhí)行哪個中斷處理程序。

3、轉(zhuǎn)移控制權(quán)

CPU會將程序的控制權(quán)轉(zhuǎn)移到中斷處理程序的入口地址,開始執(zhí)行中斷處理程序。

4、執(zhí)行中斷處理程序

中斷處理程序會根據(jù)中斷類型執(zhí)行相應(yīng)的操作,這些操作可能包括保存現(xiàn)場信息、讀取中斷事件的相關(guān)數(shù)據(jù)、執(zhí)行特定的操作,以及返回到原來的程序繼續(xù)執(zhí)行等。

5、恢復(fù)現(xiàn)場

中斷處理程序執(zhí)行完畢后,CPU會從保存的現(xiàn)場信息中恢復(fù)原來程序的狀態(tài),然后將控制權(quán)返回到原來的程序中,繼續(xù)執(zhí)行被中斷的指令。

后記

很多時候看似理所當(dāng)然的問題,當(dāng)我們深究下去就會發(fā)現(xiàn)原來別有一番天地。正如阿里王堅博士說的那樣,要想看一個人對某個領(lǐng)域的知識掌握的情況,那就看他能就這個領(lǐng)域的知識能講多長時間。想想的確如此,如果我們能夠?qū)δ硞€知識點高度提煉同時又可以細(xì)節(jié)滿滿的進(jìn)行展開闡述,那我們對于這個領(lǐng)域的理解程度就會鞭辟入里。這種檢驗自己知識學(xué)習(xí)深度的方式也推薦給大家。

責(zé)任編輯:姜華 來源: 慕楓技術(shù)筆記
相關(guān)推薦

2024-04-01 08:23:20

代碼Javajavascript

2020-04-24 16:05:06

Javascript代碼前端

2022-12-06 09:03:44

代碼fork系統(tǒng)

2023-05-09 16:23:11

人工智能ChatGPT

2021-02-27 21:45:22

程序代碼函數(shù)

2025-10-09 02:00:00

CPUMySQLJava

2021-05-10 08:20:32

CPU 符號整數(shù)

2025-03-31 08:04:50

MySQLCPU內(nèi)存

2024-09-30 10:09:52

2020-03-06 10:05:59

前端Javascript代碼

2024-08-20 09:42:38

2019-07-10 05:08:05

CPU內(nèi)存分頁管理

2018-12-06 12:58:50

CPU內(nèi)存模塊

2024-08-28 10:04:17

2023-03-02 23:09:53

Node.jsC++JS

2017-01-17 15:40:51

WIFIIEEE 802.11無線網(wǎng)絡(luò)

2018-08-16 11:30:12

JavaCPU緩存

2015-07-30 09:46:42

開源項目

2023-03-26 00:48:14

CPUSQL性能

2022-02-24 08:30:24

操作系統(tǒng)CPU程序
點贊
收藏

51CTO技術(shù)棧公眾號

亚洲精品第一国产综合精品| 久久毛片高清国产| 欧美不卡视频一区发布| 精品国产午夜福利在线观看| 在线观看男女av免费网址| 盗摄精品av一区二区三区| 日本aⅴ大伊香蕉精品视频| 青青操在线播放| 第一区第二区在线| 欧美在线观看视频一区二区 | 亚洲制服中文字幕| 国产精品原创| 综合色中文字幕| 久久久久久久久久久久久久一区| 亚洲一级特黄毛片| 国产人成精品一区二区三| 久久亚洲精品一区| 国产精品三级在线观看无码| 国产一区二区三区亚洲综合 | 国产美女无遮挡永久免费| 销魂美女一区二区三区视频在线| 久久精品福利视频| www.av天天| 免费成人三级| 欧美一区午夜视频在线观看| 成人精品小视频| 97在线超碰| 亚洲欧美另类久久久精品2019| 蜜桃麻豆www久久国产精品| 99在线精品视频免费观看20| 日本aⅴ精品一区二区三区| 性欧美xxxx视频在线观看| 一区二区三区影视| 欧美综合在线视频观看| 亚洲精品国产精品国自产在线| 99中文字幕在线| 日韩另类视频| 日韩欧美在线视频观看| 成年人午夜视频在线观看 | 成人在线免费观看网站| 精品一区二区三区电影| 亚洲av永久无码精品| 一区二区在线视频观看| 欧美一卡二卡在线| 中文字幕一区二区在线观看视频| 91欧美精品| 在线观看日产精品| 嫩草av久久伊人妇女超级a| 偷拍自拍在线看| 五月婷婷综合网| 日韩网站在线免费观看| 激情网站在线| 亚洲午夜一二三区视频| 日本人妻伦在线中文字幕| 黄色网页在线看| 亚洲桃色在线一区| www.亚洲一区二区| 亚洲91av| 亚洲成人综合在线| 国产肥臀一区二区福利视频| 在线日韩影院| 欧美综合天天夜夜久久| 国产精品区在线| 懂色aⅴ精品一区二区三区| 欧美视频三区在线播放| 天天爽人人爽夜夜爽| 久久久久久一区二区三区四区别墅| 欧美主播一区二区三区美女| 中文字幕网av| 涩涩涩久久久成人精品| 91精品国产综合久久久久久 | 国产一区二区视频网站| 日本一区中文字幕| 国产成人福利网站| 中文字幕在线观看第二页| 久久99这里只有精品| 亚洲综合自拍一区| 天堂在线观看免费视频| 久久久久88色偷偷免费| 日本免费在线视频观看| 深夜国产在线播放| 一本大道久久a久久精二百| 天美星空大象mv在线观看视频| 最新亚洲国产| 亚洲国产婷婷香蕉久久久久久| 性少妇bbw张开| 99久久精品费精品国产风间由美| 欧美日本中文字幕| 国产区一区二区三| 国产中文一区二区三区| 国产综合18久久久久久| 天堂аⅴ在线地址8| 亚洲高清免费一级二级三级| 国产裸体舞一区二区三区| 亚洲资源在线| 日韩精品在线观看网站| 免费看一级大片| 国产精品一级| 亚洲综合第一页| 麻豆app在线观看| 一区二区三区日韩在线观看| 97在线免费公开视频| 国产精品亚洲欧美一级在线| 亚洲日本欧美日韩高观看| 精品一区在线观看视频| 美女诱惑黄网站一区| 91网在线免费观看| 国产视频精选在线| 亚洲高清免费视频| 三区视频在线观看| 国产精品一线天粉嫩av| 久久免费国产精品1| 在线播放亚洲精品| 91免费观看视频| 日本福利视频网站| 亚洲精品777| 亚洲欧美中文字幕在线一区| 久久久久久福利| 麻豆精品视频在线| 欧美精品一区二区三区在线看午夜| 欧美成人三区| 欧美在线观看18| 一本加勒比北条麻妃| 一区二区视频欧美| 亚洲在线第一页| 欧美性videos| 欧美午夜不卡在线观看免费| 久久国产精品无码一级毛片| 亚洲性人人天天夜夜摸| 99re视频在线| а√天堂在线官网| 精品视频1区2区| 调教驯服丰满美艳麻麻在线视频| 国产亚洲综合精品| 国产精品视频入口| 欧美黑人猛交| 日韩午夜中文字幕| 精品国产精品国产精品| 美腿丝袜亚洲综合| 亚洲一区二区三区精品在线观看| 成年美女黄网站色大片不卡| 日韩黄色高清视频| 国产午夜小视频| 成人深夜视频在线观看| 欧美日韩激情四射| 永久免费精品视频| 久久久久久97| 全国男人的天堂网| 亚洲大片免费看| 三级黄色片网站| 久久婷婷亚洲| 日韩在线电影一区| 国产成人77亚洲精品www| 一本色道久久88综合亚洲精品ⅰ | 蜜桃网站成人| 视频二区不卡| 在线观看久久久久久| 一区二区视频网| 国产精品天美传媒沈樵| 成年网站免费在线观看| 雨宫琴音一区二区三区| 91香蕉视频在线下载| 国产精品186在线观看在线播放| 亚洲国产天堂久久国产91| 综合激情网五月| 久久久99精品免费观看| 亚洲这里只有精品| 66国产精品| 国产精品三区四区| 裤袜国产欧美精品一区| www.亚洲免费视频| 亚洲高清在线观看视频| 狠狠色狠色综合曰曰| 自拍偷拍视频亚洲| 狠狠色丁香久久婷婷综| 国产情侣第一页| 亚洲人成网站77777在线观看| 国产精品美女免费| av小次郎在线| 日韩激情在线视频| 中文在线免费观看| 亚洲一区在线观看免费观看电影高清 | 日韩中文不卡| 欧美天堂一区| 久久人人爽国产| 成人影视在线播放| 精品久久人人做人人爽| 日韩精品在线一区二区三区| 亚洲女人小视频在线观看| 91视频啊啊啊| 精品一区二区av| 日韩av综合在线观看| 国产精品99在线观看| 国产区亚洲区欧美区| 91黄页在线观看| 中文字幕久热精品在线视频| www黄色网址| 色悠久久久久综合欧美99| 日本中文在线视频| 久久久久久久久久久久久久久99| 欧美一级视频在线| 性一交一乱一区二区洋洋av| 黄色影视在线观看| 少妇精品久久久一区二区三区| 91亚洲午夜在线| 春暖花开亚洲一区二区三区| 欧美激情手机在线视频| 在线观看av黄网站永久| 日韩国产精品亚洲а∨天堂免| 国产男男gay体育生网站| 日韩欧美精品在线观看| 国产真实乱偷精品视频| 成人免费在线视频| 国产又爽又黄无码无遮挡在线观看| 国产一区二区导航在线播放| 香蕉视频网站入口| 国产亚洲激情| 欧美综合在线播放| 欧美日韩一区自拍| 一区二区三区四区五区精品| 亚洲成人一品| 好看的日韩精品| 日韩一区免费| 91精品国产综合久久久久久丝袜| 欧洲av一区二区| 韩国一区二区电影| 另类视频在线| 久精品免费视频| 久久日韩视频| 久久精品欧美视频| jzzjzzjzz亚洲成熟少妇| 亚洲美女久久久| 亚洲欧美日本在线观看| 精品国产亚洲在线| www.狠狠干| 91精品国产91热久久久做人人| 一区二区三区黄| 欧美视频在线一区二区三区| 免费av中文字幕| 色综合久久天天综合网| 国产精品suv一区二区三区| 午夜a成v人精品| av大片免费观看| 欧美日韩性生活视频| 天天干在线播放| 色综合久久天天综合网| 亚洲色成人www永久网站| 色先锋aa成人| 日本三级一区二区三区| 在线视频亚洲一区| 在线播放成人av| 欧美一区二区三区视频在线| 国产黄色免费大片| 日韩欧美国产一区在线观看| 国精产品乱码一区一区三区四区| 欧美变态tickle挠乳网站| 成人免费视频国产| 日韩av在线免费看| 国产在线黄色| 色婷婷av一区二区三区久久| 麻豆av免费在线观看| 欧美成人黄色小视频| 日本大片在线播放| 91av在线看| 性欧美freehd18| 91老司机精品视频| 91成人精品在线| 精品免费国产| 日本精品三区| 日本一二三区视频在线| 亚洲人成毛片在线播放女女| 777久久久精品一区二区三区| 视频一区二区中文字幕| 欧美一级特黄aaa| 大桥未久av一区二区三区中文| 一区二区视频观看| 中文字幕av一区二区三区免费看 | 神马午夜伦理不卡| 51精品在线观看| 亚洲电影有码| 99在线看视频| 国产中文精品久高清在线不| 精品91一区二区三区| 国产情侣一区| 天堂av手机在线| 99久久久精品免费观看国产蜜| 女人黄色一级片| 亚洲国产精品久久久男人的天堂| 中文字幕国产在线观看| 91精品国产综合久久国产大片| 人人妻人人澡人人爽久久av| 在线国产精品播放| 深夜国产在线播放| 国产精品私拍pans大尺度在线| 91蝌蚪精品视频| 日韩中文一区二区三区| 一区在线免费观看| www.国产视频.com| 久久久五月婷婷| 国产va在线播放| 欧美少妇一区二区| 国产色综合视频| 亚洲夜晚福利在线观看| freexxx性亚洲精品| 成人两性免费视频| 国产尤物久久久| 久久综合九色综合88i| 国产美女精品一区二区三区| 美女久久久久久久久久| 亚洲成a人在线观看| 在线观看中文字幕av| 亚洲欧美日韩精品久久亚洲区| 日本伦理一区二区| 国产在线久久久| 国产一区二区三区日韩精品| 精品久久久久久久久久中文字幕| 精久久久久久久久久久| 少妇人妻好深好紧精品无码| 天天免费综合色| 亚洲精品国偷拍自产在线观看蜜桃| 中文字幕精品www乱入免费视频| 国内精彩免费自拍视频在线观看网址| 97操在线视频| 久久精品影视| 91亚洲免费视频| 欧美国产一区二区| 亚洲图片在线视频| 日韩精品999| 国产精品电影| 精品一区二区视频| 一本不卡影院| 日本一区二区在线免费观看| 亚洲一区在线播放| 亚洲精品一区二区三区新线路 | 九九久久精品视频| 一级黄色录像毛片| 在线免费观看日本一区| 国产大学生校花援交在线播放 | 国产精品免费精品自在线观看| 色综合视频二区偷拍在线| 久久久久久穴| 国产手机在线观看| 色婷婷亚洲综合| 成人亚洲性情网站www在线观看| 国产成人自拍视频在线观看| 久久av超碰| 一区二区三区韩国| 国产精品久久久久天堂| 中文字幕自拍偷拍| www日韩欧美| 国产在线一区不卡| 欧美在线观看视频免费| 成人自拍视频在线| 精品91久久久| 亚洲天堂av在线免费观看| 亚洲mmav| 日韩最新中文字幕| 成人免费黄色大片| 国产手机在线视频| 亚洲精品国产品国语在线| 瑟瑟视频在线看| 日韩亚洲视频在线| 国产一区二区三区免费看| 精品爆乳一区二区三区无码av| 亚洲黄色免费三级| 韩国美女久久| 亚洲自拍偷拍一区二区三区| 福利视频网站一区二区三区| 五月天综合激情| 在线观看精品自拍私拍| 秋霞午夜一区二区三区视频| 久久久久久久午夜| 中文字幕高清不卡| 亚洲美女福利视频| 青青草原成人在线视频| 三上亚洲一区二区| 日韩大尺度视频| 在线这里只有精品| 中文字幕中文字幕在线十八区 | 青春草在线观看| 国产欧美精品在线播放| 国一区二区在线观看| 无码h肉动漫在线观看| 91精品国模一区二区三区| 三妻四妾的电影电视剧在线观看| 亚洲一区影院| 97aⅴ精品视频一二三区| 最近中文字幕在线免费观看| 久久99久国产精品黄毛片入口| 免费欧美视频| xxxx国产视频| 欧美日精品一区视频| av影视在线| 四虎免费在线观看视频| 久久久精品免费网站| 国产国语亲子伦亲子| 国产精品一区二区在线| 亚洲精品字幕| 久久久久久久久久网站| 亚洲无线码在线一区观看|