我從馮·諾依曼計(jì)算機(jī)體系,追溯到了JVM,一切原來如此!

前言

在開始前,先說為什么我從馮·諾依曼計(jì)算機(jī)體系,追溯到了JVM,一切原來如此。因?yàn)橐活w剽悍種子發(fā)現(xiàn)的一個問題,就是多數(shù)人對待知識總想問是什么,卻常常很少問為什么(或者說知道了是什么后也很少再問為什么),正如這篇所要暢談的JVM,看了很多文章"什么是JVM",我其實(shí)楞是沒搞懂什么是JVM,或者說為什么JVM是要這樣子呢?

直到回到知識歷史的起點(diǎn),再來看現(xiàn)在知識,一切原來如此。

(理解知識背后一整條脈絡(luò)遠(yuǎn)比純粹記住硬邦邦知識更為深刻,因?yàn)榍罢邥硭伎忌系挠鋹?,而后者只會讓人喪失樂趣。?/p>

誰說的?我一顆剽悍的種子眼里飽含淚水說的。

所以我回到馮·諾依曼的計(jì)算機(jī)體系再到操作系統(tǒng)去追溯 JVM 來看看這整條脈絡(luò)。

回顧計(jì)算機(jī)發(fā)展歷史,我們都知道被稱為“現(xiàn)代計(jì)算機(jī)之父”的馮·諾依曼在1945提出并奠定到至今的馮·諾依曼計(jì)算機(jī)體系,也就是計(jì)算機(jī)結(jié)構(gòu)。所以要知道一個真相是到至今為止不管在物理機(jī)之上構(gòu)建的是什么樣的操作系統(tǒng)、還是操作系統(tǒng)上構(gòu)建什么樣的虛擬機(jī),或者再上層的應(yīng)用,追溯到源頭都離不開這套體系下。(通俗的說我們被籠罩在這套體系之下)

(所以為什么你總能在JVM虛擬機(jī)身上看到計(jì)算機(jī)結(jié)構(gòu)熟悉的身影。)

計(jì)算機(jī)結(jié)構(gòu)思想

在計(jì)算機(jī)結(jié)構(gòu)中由 輸入設(shè)備、控制器、運(yùn)算器、存儲器、輸出設(shè)備 這五大部分組成。而 控制器+運(yùn)算器 就是我們再熟悉不過的CPU(中央處理器),存儲器就是內(nèi)存、磁盤等,輸入設(shè)備例如:鍵盤、鼠標(biāo),輸出設(shè)備如:顯示器等。而整個計(jì)算機(jī)結(jié)構(gòu)的運(yùn)行如下圖所示:

從上面可以看到,整個硬件已經(jīng)布好了局,當(dāng)一個程序被執(zhí)行時(shí),程序會被當(dāng)成數(shù)據(jù)一樣,所有組成結(jié)構(gòu)都可以被看成數(shù)據(jù)/指令流、控制流(圖下方箭頭所示),程序就像數(shù)據(jù)(或者指令)一樣,只要經(jīng)過流就可以被牢牢操控。這也正是馮·諾依曼的思想 “程序應(yīng)該像數(shù)據(jù)一樣可以被存儲”。

操作系統(tǒng)的本質(zhì)

在計(jì)算機(jī)結(jié)構(gòu)之上還有一層,那就是操作系統(tǒng),操作系統(tǒng)可以說是虛擬機(jī)的鼻祖,因?yàn)椴僮飨到y(tǒng)目的就是:如何操控物理機(jī)器。也就是如何將現(xiàn)實(shí)的物理資源(CPU、內(nèi)存、磁盤)虛擬化成一臺能被你通過盯著屏幕、敲著鍵盤、劃著鼠標(biāo)就能操控現(xiàn)實(shí)的物理資源。

不錯,操作系統(tǒng)本質(zhì)就是將 “物理資源虛擬化”!

(想想元宇宙的起點(diǎn)是不是從操作系統(tǒng)這里開始了呢?)

在我們最常用的操作系統(tǒng)有WindowsLinux這兩種,不過這兩個操作系統(tǒng)之間是不兼容的,那么就會存在編寫的程序并不能同時(shí)運(yùn)行在兩個操作系統(tǒng)上,而開發(fā)程序時(shí)就得要分別開發(fā)。那么就會觸發(fā)人類(程序員)的第一生產(chǎn)力 !

千萬別笑!人類發(fā)明蒸汽機(jī)以來,就從未停止過釋放重復(fù)勞動力的工作,人類發(fā)明洗衣機(jī)替代洗衣、洗碗機(jī)替代洗碗、汽車替代馬車,以及再到20世紀(jì)30年代就提出,但至今仍在火的自動駕駛即將替代司機(jī)。但是它們并不具備通用性,就像問鼎了圍棋界的谷歌機(jī)器 AlphaGo ,就算再厲害也只會下圍棋這一件事(圍棋曾被認(rèn)為是計(jì)算機(jī)沒法逾越的天花板,但被AlphaGo戰(zhàn)勝,不過AlphaGo也只是屬于弱人工智能),可是從整個人類科技發(fā)展來看,人類正在一步步開發(fā)著具備更通用性的人工智能,也就是有一天超越強(qiáng)人工智能而跨越起點(diǎn)的超人工智能,相信未來程序員唯一能做可能就是一段對話:

程序員:“ 嘿 Siri*:幫我開發(fā)一個掘金,順便幫我安排一個叫 一顆剽悍的種子 的家伙寫一篇JVM?!?/p>

程序員:“嘿,對了,Siri,看文章前別忘了給那家伙一鍵三連?!?/p>

Siri:“f*ck 又提需求?!?/p>

程序員:“什么?”

Siri:“安排”

(扯遠(yuǎn)了題外話,希望你能Get到人類追求通用性(懶)遠(yuǎn)不止在JVM中體現(xiàn)。)

JVM的全稱Java Virtual Machine也就是Java虛擬機(jī),拋開Java虛擬機(jī)前綴Java,虛擬機(jī)其實(shí)就是在“虛擬(抽象)計(jì)算機(jī)”,也就是在操作系統(tǒng)之上再次虛擬出一臺計(jì)算機(jī),來屏蔽不同硬件和操作系統(tǒng)之間的差異(如果說操作系統(tǒng)是用戶與物理資源之間的橋梁,那么JVM就是不同硬件和操作系統(tǒng)上的橋梁),JVM目的就是具備通用性,也就是我們常說的Java可以跨平臺開發(fā)。

那么回到JVM這時(shí)容易懂了,JVM可以像計(jì)算機(jī)結(jié)構(gòu)一樣去運(yùn)行所編寫的程序,而JVM主要由:類裝載器、運(yùn)行時(shí)數(shù)據(jù)區(qū)、執(zhí)行引擎、本地庫接口這四個部分所構(gòu)成。

我們知道一個可運(yùn)行的程序其實(shí)不過是我們所定義的一個個 .java源代碼文件組成(這些文件其實(shí)就是我們程序員對于事物的抽象,然后交由計(jì)算機(jī)幫我們實(shí)現(xiàn))。而JVM只規(guī)定.class字節(jié)碼文件格式,這樣的好處很明顯,那就是不僅只是Java可以運(yùn)行在JVM上,而是只要是能生成.class字節(jié)碼文件也都可以運(yùn)行在JVM之上,所以Java虛擬機(jī)并不是Java語言專有的。所以JVM通過字節(jié)碼存儲格式統(tǒng)一了所有平臺,字節(jié)碼是構(gòu)成平臺無關(guān)性的基石。如:Python、Ruby、Scala等語言(可以說JVM野心真大?。?。

所以.java文件想要在JVM上被執(zhí)行,就必須先由javac編譯器轉(zhuǎn)成后.class再交給Class Loader 類裝載器去進(jìn)行類的加載等。而這也相當(dāng)于開始像計(jì)算機(jī)結(jié)構(gòu)一樣的輸入設(shè)備正式的進(jìn)去到整個機(jī)器內(nèi)部。如下圖所示:

類裝載器執(zhí)行過程

說到虛擬機(jī)類裝載器,就不得不說類加載的機(jī)制,而一套機(jī)制就一定會有一個過程,也就是一個類進(jìn)入入口(類裝載器)到虛擬機(jī)中必經(jīng)的整個生命周期:Loading 加載、Linking 連接、Initialization 初始化、Using 使用、Unlading 卸載。如下圖所示:

但其中最主要的Class文件裝載過程是加載、連接、初始化這三步。

加載

類加載過程的第一個過程毫無疑問是加載,如果虛擬機(jī)還沒有加載過此類,會通過類加載器將字節(jié)碼文件加載到內(nèi)存中。當(dāng)然也可以是從ZIP壓縮包,或者JAR、WAR等格式,最終也不過是從中取出類文件而已。

連接

雖然任何二進(jìn)制都可以是Class類型,但是只有JVM能夠裝載的Class文件類型才能運(yùn)行在JVM之上。(也就是要符合虛擬機(jī)的規(guī)范的字節(jié)碼文件才能通過)加載后是連接的過程(連接通俗的說就是將類文件與虛擬機(jī)建立關(guān)聯(lián)),從上圖可以看到連接的包括了三個過程:驗(yàn)證、準(zhǔn)備、解析。 連接的第一步是 驗(yàn)證,驗(yàn)證會從四個階段的檢驗(yàn)依次進(jìn)行:

驗(yàn)證

驗(yàn)證的四個階段:

  1. 文件格式驗(yàn)證,其實(shí)就是檢查是否符合Class文件規(guī)范,主要有:魔數(shù)檢查、版本檢查等等,例如:魔數(shù)檢查就是看Class文件開頭是否是 0xCAFEBABE 開頭(魔數(shù)通俗的說就是打一個讓JVM認(rèn)識的標(biāo)簽)。版本檢查就是看Class文件主次版本號是否能在當(dāng)前版本虛擬機(jī)處理等等。

  2. 元數(shù)據(jù)驗(yàn)證,(元數(shù)據(jù)可能不好理解,其實(shí)元數(shù)據(jù)從字面意思上就是 描述數(shù)據(jù)的數(shù)據(jù),所以元數(shù)據(jù)驗(yàn)證就是語義的檢查)檢查類中是否被final修飾、是否有繼承了父類等等。

  3. 字節(jié)碼驗(yàn)證,要做的就是確定程序中的語義是否合法且符合邏輯的(通過分析數(shù)據(jù)流和控制流),確保跳轉(zhuǎn)指令指向正確位置,操作數(shù)類型合理等等。

  4. 符號引用驗(yàn)證,先說什么是符號引用?符號引用是以一組符號來描述所引用的目標(biāo),可以是任何字面量。(聽到這你可能還不能Get到的話,暖男一顆剽悍的種子,下面舉個例子,你就悟了)

在上面我們知道 .class 文件是經(jīng)過編譯器編譯后的文件,而 .class 文件里面的內(nèi)容就是字節(jié)碼,通過字節(jié)碼中記錄著自己將要使用的其他類或方法等。

例如下面這段代碼,我們定義一個字符串類型變量的 userName,通過 System.out.println() 打印。

String userName = "Java碼農(nóng)";

System.out.println(userName);

我們看上面代碼編譯成字節(jié)碼后是什么樣子的,如下所示:

 0 ldc #2 <Java碼農(nóng)>
 2 astore_1
 3 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>
 6 aload_1
 7 invokevirtual #4 <java/io/PrintStream.println : (Ljava/lang/String;)V>
10 return

可以看到在代碼中最常見的表達(dá)式:

System.out.println(userName)

而轉(zhuǎn)換后的字節(jié)碼中是使用符號引用來“代替”表達(dá):

invokevirtual #4 <java/io/PrintStream.println : (Ljava/lang/String;)V>

所以符號引用驗(yàn)證就是通過檢查符號引用所“代替”的類、屬性、方法是否存在且有權(quán)限被訪問。

上面的驗(yàn)證東西很多,但是不管什么樣的驗(yàn)證,都是為了 確保類符合 Java 規(guī)范與符合 JVM 規(guī)范,同時(shí) 避免危害到虛擬機(jī)的安全

準(zhǔn)備

連接的第二步是 準(zhǔn)備,這個階段主要做兩件事,為通過驗(yàn)證了的類來分配內(nèi)存空間設(shè)置初始化。

如對于 final static 修飾的變量,會直接賦值我們的定義值??梢钥聪旅孢@段代碼,會在準(zhǔn)備階段分配內(nèi)存,并初始化值。

private final static String value = "一顆剽悍的種子"

各數(shù)據(jù)類型默認(rèn)初始值,如下圖所示:

(注意:上圖中并沒有 boolean 類型,Java中的 boolean 類型的底層實(shí)現(xiàn)實(shí)際上就是 int 類型,int 類型默認(rèn)值 0,對應(yīng)的就是 boolean 類型默認(rèn)值 false。)

解析

連接的第三步是 解析,解析階段的工作就是將 符號引用轉(zhuǎn)為直接引用。因?yàn)樵诰幾g時(shí)類、方法等都是用符號引用來代替(所以為什么叫符號引用,符號只是個標(biāo)識),而符號引用是并不知道這些數(shù)據(jù)所引用的 實(shí)際地址

所以如果僅僅用符號引用就面臨一個問題,就是 不能確定一定存在該對象。所以通過解析將符號引用轉(zhuǎn)化為 JVM可直接獲取的內(nèi)存地址或指針,也就是 直接引用。

當(dāng)解析將符號引用轉(zhuǎn)成直接引用時(shí),也就是目標(biāo)必定已經(jīng)在虛擬機(jī)的內(nèi)存中存在(說白了用直接引用就是確定了存在該類、方法或?qū)傩裕?/p>

初始化

類裝載過程中最后階段是初始化。而這個階段將會執(zhí)行構(gòu)造器<clinit>方法,它是在通過我們前面提到過的Javac.java 文件編譯成 .class 字節(jié)碼文件時(shí),所有類初始化代碼,也就是包括靜態(tài)變量賦值語句、靜態(tài)代碼塊、靜態(tài)方法,收集在一起后成為 <clinit>() 方法。

簡單的概括初始化目的就是 初始化給類靜態(tài)變量或靜態(tài)代碼塊為程序員自己所定義的值

到此,類的加載過程就像馮·諾依曼計(jì)算機(jī)結(jié)構(gòu)中的輸入設(shè)備,負(fù)責(zé)將數(shù)據(jù)丟進(jìn)了入口后就是真正到JVM內(nèi)部(JVM運(yùn)行時(shí)數(shù)據(jù)區(qū))去操縱數(shù)據(jù),直至將我們的想法通過代碼最后交給機(jī)器來完成。

JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)

JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)主要分為 堆、程序計(jì)數(shù)器、方法區(qū)、虛擬機(jī)棧和本地方法棧 這五個分區(qū)。其中按 線程共享線程私有 兩類:

  • 線程共享:堆、方法區(qū)。

  • 線程私有:程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧。

在JVM 內(nèi)存中最大的一塊內(nèi)存空間就是堆,而且堆也被所有線程共享,所以堆也幾乎存儲著所有的對象。堆被按年代進(jìn)行劃分為 新時(shí)代、 老年代 以及 持久代,新生代又接著被分為 Eden (伊甸園區(qū)) 和 Surivor (幸存區(qū))。而 Surivor 進(jìn)一步由 From SurvivorTo Survivor 進(jìn)行劃分。

JDK 8之前以及之后堆按年代劃分的變化,如下圖所示:

方法區(qū)

方法區(qū)跟堆一樣是線程共享的區(qū)域,當(dāng).class字節(jié)碼文件在JVM加載時(shí)會被分配到不同的數(shù)據(jù)結(jié)構(gòu),如常量池、方法、構(gòu)造函數(shù),同時(shí)也主要包括用來存儲已被虛擬機(jī)加載的類相關(guān)信息(類信息又包括了類的版本、字段、方法、接口和父類等信息)都存放在方法區(qū)。

程序計(jì)數(shù)器

我們知道Java程序是多線程執(zhí)行的,所以即想要能滿足多個線程的交叉執(zhí)行,又想要確保多個線程都能完整的執(zhí)行完各自的工作,那么一旦出現(xiàn)被中斷的線程,線程執(zhí)行到哪條的內(nèi)存地址(指令)就必須被保存下來,這樣當(dāng)被中斷的線程恢復(fù)時(shí)就又可以接著執(zhí)行下去。

而這就是程序計(jì)數(shù)器的工作,用來記錄哪個線程當(dāng)前執(zhí)行到哪條指令。所以分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等都需要依賴計(jì)數(shù)器完成。(形象的說就是程序控制流的指示器)

虛擬機(jī)棧

虛擬機(jī)棧是線程私有的區(qū)域,所以不用關(guān)心數(shù)據(jù)一致性問題。當(dāng)我們創(chuàng)建一個線程時(shí),同樣在JVM中也會創(chuàng)建一個與之對應(yīng)的棧,稱為虛擬機(jī)棧。

而虛擬機(jī)棧的內(nèi)部其實(shí)是一個或多個的棧幀,每一個棧幀又都對應(yīng)著一個Java方法的調(diào)用。

其運(yùn)行過程是,當(dāng)我們創(chuàng)建一個新方法同時(shí),與之對應(yīng)會在虛擬機(jī)棧中同樣創(chuàng)建一個新的棧幀(當(dāng)前棧幀)會被放在棧頂(只要是棧就會有棧頂和棧底),同時(shí)程序計(jì)數(shù)器也會指向這個當(dāng)前棧地址。如下圖所示:

在每個棧幀里又存儲著方法的 局部變量表、操作數(shù)棧、動態(tài)鏈接、方法返回地址、附加信息 參與著方法的調(diào)用和返回。

所以 如果說堆解決了數(shù)據(jù)存儲的問題,那么棧就是解決了程序如何運(yùn)行的問題。

本地方法棧

本地方法棧是為了運(yùn)行JVM本地方法(也就是 Native 方法)而準(zhǔn)備的空間。而從字面上之所以被稱為本地方法棧,也是因?yàn)?Natice 方法很多也都是由C語言所實(shí)現(xiàn)的。

如果說 “類加載子系統(tǒng)是計(jì)算機(jī)結(jié)構(gòu)中的輸入設(shè)備,那么運(yùn)行時(shí)數(shù)據(jù)區(qū)就是計(jì)算機(jī)結(jié)構(gòu)中的CPU(控制器和運(yùn)算器)和存儲器,那么最后的輸出設(shè)備就是執(zhí)行引擎?!?/strong>

執(zhí)行引擎

到了執(zhí)行引擎,可以說是JVM最后的一個環(huán)節(jié),從最先的一個Java源文件編譯成.class字節(jié)碼文件經(jīng)過了類加載子系統(tǒng),也經(jīng)過了上面的JVM運(yùn)行時(shí)數(shù)據(jù)區(qū),經(jīng)過了這一整系列下來,通俗的說字節(jié)碼文件已經(jīng)被重新打碎重組成了一個可以由JVM所操控的一系列數(shù)據(jù)(再回看計(jì)算機(jī)結(jié)構(gòu),數(shù)據(jù)成流,被布好的局安排的明明白白),但是問題是代碼并不能被執(zhí)行。因?yàn)镴VM并不是將高級語言直接轉(zhuǎn)成機(jī)器指令,而是字節(jié)碼,所以字節(jié)碼的真正的運(yùn)行得由執(zhí)行引擎去將字節(jié)碼翻譯成機(jī)器指令后,由真正物理機(jī)去執(zhí)行。

但是我們知道JVM只是對計(jì)算機(jī)的抽象,它的一切都只是建立在軟件層面自行實(shí)現(xiàn)的。而物理計(jì)算機(jī)只認(rèn)識機(jī)器碼指令,這些機(jī)器碼指令運(yùn)行通過處理器、緩存、指令集和操作系統(tǒng)等構(gòu)建了物理機(jī)的執(zhí)行引擎。

所以JVM想要讓Java程序運(yùn)行起來,同樣也是要在 “虛擬(通俗說是模擬)一個執(zhí)行引擎”,而執(zhí)行引擎的目的就是 將字節(jié)碼指令解釋(編譯)為機(jī)器指令。

因?yàn)榛氐奖举|(zhì),真正干活做事的是物理機(jī),所以執(zhí)行引擎就是將字節(jié)碼轉(zhuǎn)成為物理機(jī)可執(zhí)行的機(jī)器碼(從用戶看執(zhí)行引擎就是一臺翻譯機(jī))

我們使用的HotSpot VM是目前虛擬機(jī)的代表之一,它是集解釋器和JIT即時(shí)編譯器于一身 的架構(gòu)。也就說Java虛擬機(jī)運(yùn)行時(shí),解釋器JIT(just in time 即時(shí)編譯器) 互相協(xié)作。

解釋器

在JVM早期使用的就是解釋器(大多數(shù)語言同樣也是),解釋器就是在運(yùn)行時(shí)逐行解釋字節(jié)碼轉(zhuǎn)化成機(jī)器碼再執(zhí)行程序。(這也解釋了上面所說,JVM為什么不直接將Java語言直接轉(zhuǎn)化為機(jī)器指碼指令直接就能在物理機(jī)上執(zhí)行。而是要通過加多一層字節(jié)碼文件來具備通用性,所以以這種 “加一個翻譯器”的方式,來避免高級語言直接轉(zhuǎn)成本地機(jī)器指令的耦合,重要的事情不要忘了,JVM虛擬機(jī)是一個概念,也不要忘了目的是具備通用性)

在Java發(fā)展路程中,從最早期的,也是最古老的字節(jié)碼解釋器。之后到了目前普遍使用的模板解釋器。一共有兩套解釋執(zhí)行器。字節(jié)碼解釋器是在執(zhí)行時(shí)通過純軟件代碼模擬字節(jié)碼的執(zhí)行,所以效率也非常低。而模板解釋器是將每一條字節(jié)碼和模板函數(shù)相關(guān)聯(lián),而模板函數(shù)能直接產(chǎn)生這條字節(jié)碼執(zhí)行時(shí)的機(jī)器碼,從而達(dá)到提高解釋器性能。

但是單憑字節(jié)碼解釋器效率還遠(yuǎn)不夠,所以 為了追求一把即時(shí)速度的推背感,虛擬機(jī)又加上了JIT也就是即時(shí)編譯器。

JIT 即時(shí)編譯器

從上面我們知道在JVM執(zhí)行引擎擁有字節(jié)碼解釋器之后又加入了JIT,而使用JIT對字節(jié)碼轉(zhuǎn)化為機(jī)器碼指令時(shí),關(guān)注的核心一點(diǎn)就是 程序中運(yùn)行時(shí)被調(diào)用頻繁的代碼,被稱為 “熱點(diǎn)代碼”。

而要找到這些 “熱點(diǎn)代碼”就需要使用到JIT的 熱點(diǎn)探測。目前的Host Spot 的JVM采用的熱點(diǎn)探測是基于 計(jì)數(shù)器熱點(diǎn)探測。計(jì)數(shù)器熱點(diǎn)探測很好理解,就是統(tǒng)計(jì)每個方法執(zhí)行次數(shù),當(dāng)超過認(rèn)為的熱點(diǎn)閾值,那么就屬于“熱點(diǎn)代碼”。

計(jì)數(shù)器熱點(diǎn)探測被分為 方法調(diào)用計(jì)數(shù)器回邊計(jì)數(shù)器 兩類。方法調(diào)用計(jì)數(shù)器用來統(tǒng)計(jì)代碼調(diào)用次數(shù),而回邊計(jì)數(shù)器則用來統(tǒng)計(jì)循環(huán)執(zhí)行次數(shù)。

方法調(diào)用的計(jì)數(shù)器除了遞增,也同樣有熱度衰減,也就是當(dāng)代碼調(diào)用次數(shù)超過一定時(shí)間已經(jīng)不足提交給JIT,那么調(diào)用計(jì)數(shù)器會遞減。

心細(xì)的朋友會發(fā)現(xiàn)跟前面的 Redis的內(nèi)存淘汰策略中的LFU算法很類似,在之前文章詳盡的闡述,如果感興趣可以去翻看,這里就不再贅述。

Redis是怎么解決緩存占滿內(nèi)存的?

而回邊計(jì)數(shù)器的主要目的是為了觸發(fā) OSR (On StackReplacement)棧上編譯。在一些循環(huán)周期較長代碼會在循環(huán)時(shí)間內(nèi),會直接將代碼替換執(zhí)行緩存機(jī)器碼。

本地方法接口與本地方法庫

在JVM中如果需要與一些底層系統(tǒng)實(shí)現(xiàn)交互,那么就會使用到本地方法接口與本地方法庫,也就是 Native Method,本地方法接口與本地方法庫其目很簡單,就是借用到CC++等其他語言的資源。

總結(jié)

我們從馮諾依曼計(jì)算機(jī)體系理解了計(jì)算機(jī)結(jié)構(gòu)思想 程序應(yīng)該像數(shù)據(jù)一樣可以被存儲,也就是說程序就像數(shù)據(jù)(或者指令)一樣,只要經(jīng)過像組件構(gòu)建的流就可以被牢牢操控。而接著探究操作系統(tǒng)本質(zhì)其實(shí)就是將物理資源虛擬化,可以說是用戶與物理資源之間的橋梁。最后追溯到JVM其實(shí)就是在一臺虛擬(抽象)的計(jì)算機(jī),如果類加載子系統(tǒng)是計(jì)算機(jī)結(jié)構(gòu)中的輸入設(shè)備,那么運(yùn)行時(shí)數(shù)據(jù)區(qū)就是計(jì)算機(jī)結(jié)構(gòu)中的CPU(控制器和運(yùn)算器)和存儲器,那么到最終的輸出設(shè)備就是執(zhí)行引擎。

到此,JVM還有類加載的雙親委派機(jī)制,以及JVM的垃圾回收機(jī)制、JVM的堆棧等異常以及JVM配置參數(shù)等內(nèi)容沒有聊,因?yàn)檫@些內(nèi)容都值得再獨(dú)立拎出來細(xì)說。所以這一篇文章可以當(dāng)成JVM的一個開篇,也可以說是JVM的一張地圖。地圖作用不是告訴你應(yīng)該去哪的路標(biāo),而是能縱覽整個全貌后,至于你對這整個知識是想怎么理解的都取決于的是你自己的思考。

好了,這兩個多星期就到這了。

好了,其實(shí)是這一個多月就到這了。

(最后,我們至今還沒有非馮·諾依曼體系下的新計(jì)算機(jī)結(jié)構(gòu),但不妨大膽設(shè)想未來,也許會在距實(shí)用還相去甚遠(yuǎn)的量子計(jì)算機(jī)上看到呢。)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容