Java虛擬機(jī)(JVM)是可運(yùn)行Java代碼的假想計(jì)算機(jī) 只要根據(jù)JVM規(guī)格描述將解釋器移植到特定的計(jì)算機(jī)上 就能保證經(jīng)過編譯的任何Java代碼能夠在該系統(tǒng)上運(yùn)行 本文首先簡(jiǎn)要介紹從Java文件的編譯到最終執(zhí)行的過程 隨后對(duì)JVM規(guī)格描述作一說明 一 Java源文件的編譯 下載 解釋和執(zhí)行 Java應(yīng)用程序的開發(fā)周期包括編譯 下載 解釋和執(zhí)行幾個(gè)部分 Java編譯程序?qū)ava源程序翻譯為JVM可執(zhí)行代碼?字節(jié)碼 這一編譯過程同C/C++的編譯有些不同 當(dāng)C編譯器編譯生成一個(gè)對(duì)象的代碼時(shí) 該代碼是為在某一特定硬件平臺(tái)運(yùn)行而產(chǎn)生的 因此 在編譯過程中 編譯程序通過查表將所有對(duì)符號(hào)的引用轉(zhuǎn)換為特定的內(nèi)存偏移量 以保證程序運(yùn)行 Java編譯器卻不將對(duì)變量和方法的引用編譯為數(shù)值引用 也不確定程序執(zhí)行過程中的內(nèi)存布局 而是將這些符號(hào)引用信息保留在字節(jié)碼中 由解釋器在運(yùn)行過程中創(chuàng)立內(nèi)存布局 然后再通過查表來確定一個(gè)方法所在的地址 這樣就有效的保證了Java的可移植性和安全性 運(yùn)行JVM字節(jié)碼的工作是由解釋器來完成的 解釋執(zhí)行過程分三部進(jìn)行 代碼的裝入 代碼的校驗(yàn)和代碼的執(zhí)行 裝入代碼的工作由 類裝載器 (class loader)完成 類裝載器負(fù)責(zé)裝入運(yùn)行一個(gè)程序需要的所有代碼 這也包括程序代碼中的類所繼承的類和被其調(diào)用的類 當(dāng)類裝載器裝入一個(gè)類時(shí) 該類被放在自己的名字空間中 除了通過符號(hào)引用自己名字空間以外的類 類之間沒有其他辦法可以影響其他類 在本臺(tái)計(jì)算機(jī)上的所有類都在同一地址空間內(nèi) 而所有從外部引進(jìn)的類 都有一個(gè)自己獨(dú)立的名字空間 這使得本地類通過共享相同的名字空間獲得較高的運(yùn)行效率 同時(shí)又保證它們與從外部引進(jìn)的類不會(huì)相互影響 當(dāng)裝入了運(yùn)行程序需要的所有類后 解釋器便可確定整個(gè)可執(zhí)行程序的內(nèi)存布局 解釋器為符號(hào)引用同特定的地址空間建立對(duì)應(yīng)關(guān)系及查詢表 通過在這一階段確定代碼的內(nèi)存布局 Java很好地解決了由超類改變而使子類崩潰的問題 同時(shí)也防止了代碼對(duì)地址的非法訪問 隨后 被裝入的代碼由字節(jié)碼校驗(yàn)器進(jìn)行檢查 校驗(yàn)器可發(fā)現(xiàn)操作數(shù)棧溢出 非法數(shù)據(jù)類型轉(zhuǎn)化等多種錯(cuò)誤 通過校驗(yàn)后 代碼便開始執(zhí)行了 Java字節(jié)碼的執(zhí)行有兩種方式 即時(shí)編譯方式 解釋器先將字節(jié)碼編譯成機(jī)器碼 然后再執(zhí)行該機(jī)器碼 解釋執(zhí)行方式 解釋器通過每次解釋并執(zhí)行一小段代碼來完成Java字節(jié)碼程 序的所有操作 通常采用的是第二種方法 由于JVM規(guī)格描述具有足夠的靈活性 這使得將字節(jié)碼翻譯為機(jī)器代碼的工作具有較高的效率 對(duì)于那些對(duì)運(yùn)行速度要求較高的應(yīng)用程序 解釋器可將Java字節(jié)碼即時(shí)編譯為機(jī)器碼 從而很好地保證了Java代碼的可移植性和高性能 二 JVM規(guī)格描述 JVM的設(shè)計(jì)目標(biāo)是提供一個(gè)基于抽象規(guī)格描述的計(jì)算機(jī)模型 為解釋程序開發(fā)人員提很好的靈活性 同時(shí)也確保Java代碼可在符合該規(guī)范的任何系統(tǒng)上運(yùn)行 JVM對(duì)其實(shí)現(xiàn)的某些方面給出了具體的定義 特別是對(duì)Java可執(zhí)行代碼 即字節(jié)碼(Bytecode)的格式給出了明確的規(guī)格 這一規(guī)格包括操作碼和操作數(shù)的語法和數(shù)值 標(biāo)識(shí)符的數(shù)值表示方式 以及Java類文件中的Java對(duì)象 常量緩沖池在JVM的存儲(chǔ)映象 這些定義為JVM解釋器開發(fā)人員提供了所需的信息和開發(fā)環(huán)境 Java的設(shè)計(jì)者希望給開發(fā)人員以隨心所欲使用Java的自由 JVM定義了控制Java代碼解釋執(zhí)行和具體實(shí)現(xiàn)的五種規(guī)格 它們是 JVM指令系統(tǒng)JVM寄存器JVM棧結(jié)構(gòu)JVM碎片回收堆JVM存儲(chǔ)區(qū) JVM指令系統(tǒng)JVM指令系統(tǒng)同其他計(jì)算機(jī)的指令系統(tǒng)極其相似 Java指令也是由 操作碼和操作數(shù)兩部分組成 操作碼為 位二進(jìn)制數(shù) 操作數(shù)進(jìn)緊隨在操作碼的后面 其長(zhǎng)度根據(jù)需要而不同 操作碼用于指定一條指令操作的性質(zhì)(在這里我們采用匯編符號(hào)的形式進(jìn)行說明) 如iload表示從存儲(chǔ)器中裝入一個(gè)整數(shù) anewarray表示為一個(gè)新數(shù)組分配空間 iand表示兩個(gè)整數(shù)的 與 ret用于流程控制 表示從對(duì)某一方法的調(diào)用中返回 當(dāng)長(zhǎng)度大于 位時(shí) 操作數(shù)被分為兩個(gè)以上字節(jié)存放 JVM采用了 big endian 的編碼方式來處理這種情況 即高位bits存放在低字節(jié)中 這同 Motorola及其他的RISC CPU采用的編碼方式是一致的 而與Intel采用的 little endian 的編碼方式即低位bits存放在低位字節(jié)的方法不同 Java指令系統(tǒng)是以Java語言的實(shí)現(xiàn)為目的設(shè)計(jì)的 其中包含了用于調(diào)用方法和監(jiān)視多先程系統(tǒng)的指令 Java的 位操作碼的長(zhǎng)度使得JVM最多有 種指令 目前已使用了 多種操作碼 JVM指令系統(tǒng)所有的CPU均包含用于保存系統(tǒng)狀態(tài)和處理器所需信息的寄存器組 如果虛擬機(jī)定義較多的寄存器 便可以從中得到更多的信息而不必對(duì)?;騼?nèi)存進(jìn)行訪問 這有利于提高運(yùn)行速度 然而 如果虛擬機(jī)中的寄存器比實(shí)際CPU的寄存器多 在實(shí)現(xiàn)虛擬機(jī)時(shí)就會(huì)占用處理器大量的時(shí)間來用常規(guī)存儲(chǔ)器模擬寄存器 這反而會(huì)降低虛擬機(jī)的效率 針對(duì)這種情況 JVM只設(shè)置了 個(gè)最為常用的寄存器 它們是 pc程序計(jì)數(shù)器optop操作數(shù)棧頂指針frame當(dāng)前執(zhí)行環(huán)境指針vars指向當(dāng)前執(zhí)行環(huán)境中第一個(gè)局部變量的指針?biāo)屑拇嫫骶鶠?位 pc用于記錄程序的執(zhí)行 optop frame和vars用于記錄指向Java棧區(qū)的指針 JVM棧結(jié)構(gòu)作為基于棧結(jié)構(gòu)的計(jì)算機(jī) Java棧是JVM存儲(chǔ)信息的主要方法 當(dāng)JVM得到一個(gè)Java字節(jié)碼應(yīng)用程序后 便為該代碼中一個(gè)類的每一個(gè)方法創(chuàng)建一個(gè)??蚣?以保存該方法的狀態(tài)信息 每個(gè)??蚣馨ㄒ韵氯愋畔?局部變量執(zhí)行環(huán)境操作數(shù)棧局部變量用于存儲(chǔ)一個(gè)類的方法中所用到的局部變量 vars寄存器指向該變量表中的第一個(gè)局部變量 執(zhí)行環(huán)境用于保存解釋器對(duì)Java字節(jié)碼進(jìn)行解釋過程中所需的信息 它們是 上次調(diào)用的方法 局部變量指針和操作數(shù)棧的棧頂和棧底指針 執(zhí)行環(huán)境是一個(gè)執(zhí)行一個(gè)方法的控制中心 例如 如果解釋器要執(zhí)行iadd(整數(shù)加法) 首先要從frame寄存器中找到當(dāng)前執(zhí)行環(huán)境 而后便從執(zhí)行環(huán)境中找到操作數(shù)棧 從棧頂彈出兩個(gè)整數(shù)進(jìn)行加法運(yùn)算 最后將結(jié)果壓入棧頂 操作數(shù)棧用于存儲(chǔ)運(yùn)算所需操作數(shù)及運(yùn)算的結(jié)果 JVM碎片回收堆Java類的實(shí)例所需的存儲(chǔ)空間是在堆上分配的 解釋器具體承擔(dān)為類實(shí)例分配空間的工作 解釋器在為一個(gè)實(shí)例分配完存儲(chǔ)空間后 便開始記錄對(duì)該實(shí)例所占用的內(nèi)存區(qū)域的使用 一旦對(duì)象使用完畢 便將其回收到堆中 在Java語言中 除了new語句外沒有其他方法為一對(duì)象申請(qǐng)和釋放內(nèi)存 對(duì)內(nèi)存進(jìn)行釋放和回收的工作是由Java運(yùn)行系統(tǒng)承擔(dān)的 這允許Java運(yùn)行系統(tǒng)的設(shè)計(jì)者自己決定碎片回收的方法 在SUN公司開發(fā)的Java解釋器和Hot Java環(huán)境中 碎片回收用后臺(tái)線程的方式來執(zhí)行 這不但為運(yùn)行系統(tǒng)提供了良好的性能 而且使程序設(shè)計(jì)人員擺脫了自己控制內(nèi)存使用的風(fēng)險(xiǎn) JVM存儲(chǔ)區(qū)JVM有兩類存儲(chǔ)區(qū) 常量緩沖池和方法區(qū) 常量緩沖池用于存儲(chǔ)類名稱 方法和字段名稱以及串常量 方法區(qū)則用于存儲(chǔ)Java方法的字節(jié)碼 對(duì)于這兩種存儲(chǔ)區(qū)域具體實(shí)現(xiàn)方式在JVM規(guī)格中沒有明確規(guī)定 這使得Java應(yīng)用程序的存儲(chǔ)布局必須在運(yùn)行過程中確定 依賴于具體平臺(tái)的實(shí)現(xiàn)方式 JVM是為Java字節(jié)碼定義的一種獨(dú)立于具體平臺(tái)的規(guī)格描述 是Java平臺(tái)獨(dú)立性的基礎(chǔ) 目前的JVM還存在一些限制和不足 有待于進(jìn)一步的完善 但無論如何 JVM的思想是成功的 對(duì)比分析 如果把Java原程序想象成我們的C++原程序 Java原程序編譯后生成的字節(jié)碼就相當(dāng)于C++原程序編譯后的 x 的機(jī)器碼(二進(jìn)制程序文件) JVM虛擬機(jī)相當(dāng)于 x 計(jì)算機(jī)系統(tǒng) Java解釋器相當(dāng)于 x CPU 在 x CPU上運(yùn)行的是機(jī)器碼 在Java解釋器上運(yùn)行的是Java字節(jié)碼 Java解釋器相當(dāng)于運(yùn)行Java字節(jié)碼的 CPU 但該 CPU 不是通過硬件實(shí)現(xiàn)的 而是用軟件實(shí)現(xiàn)的 Java解釋器實(shí)際上就是特定的平臺(tái)下的一個(gè)應(yīng)用程序 只要實(shí)現(xiàn)了特定平臺(tái)下的解釋器程序 Java字節(jié)碼就能通過解釋器程序在該平臺(tái)下運(yùn)行 這是Java跨平臺(tái)的根本 當(dāng)前 并不是在所有的平臺(tái)下都有相應(yīng)Java解釋器程序 這也是Java并不能在所有的平臺(tái)下都能運(yùn)行的原因 它只能在已實(shí)現(xiàn)了Java解釋器程序的平臺(tái)下運(yùn)行 lishixinzhi/Article/program/Java/hx/201311/26222
成都創(chuàng)新互聯(lián)是一家專注于成都做網(wǎng)站、網(wǎng)站制作與策劃設(shè)計(jì),袁州網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)十載,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:袁州等地區(qū)。袁州做網(wǎng)站價(jià)格咨詢:13518219792
Java編譯原理:
Java 虛擬機(jī)(JVM)是可運(yùn)行Java 代碼的假想計(jì)算機(jī)。只要根據(jù)JVM規(guī)格描述將解釋器移植到特定的計(jì)算機(jī)上,就能保證經(jīng)過編譯的任何Java代碼能夠在該系統(tǒng)上運(yùn)行。
一.Java源文件的編譯、下載 、解釋和執(zhí)行
Java應(yīng)用程序的開發(fā)周期包括編譯、下載 、解釋和執(zhí)行幾個(gè)部分。Java編譯程序?qū)ava源程序翻譯為JVM可執(zhí)行代碼?字節(jié)碼。這一編譯過程同C/C++ 的編譯有些不同。當(dāng)C編譯器編譯生成一個(gè)對(duì)象的代碼時(shí),該代碼是為在某一特定硬件平臺(tái)運(yùn)行而產(chǎn)生的。因此,在編譯過程中,編譯程序通過查表將所有對(duì)符號(hào)的引用轉(zhuǎn)換為特定的內(nèi)存偏移量,以保證程序運(yùn)行。Java編譯器卻不將對(duì)變量和方法的引用編譯為數(shù)值引用,也不確定程序執(zhí)行過程中的內(nèi)存布局,而是將這些符號(hào)引用信息保留在字節(jié)碼中,由解釋器在運(yùn)行過程中創(chuàng)立內(nèi)存布局,然后再通過查表來確定一個(gè)方法所在的地址。這樣就有效的保證了Java的可移植性和安全 性。
運(yùn)行JVM字節(jié)碼的工作是由解釋器來完成的。解釋執(zhí)行過程分三部進(jìn)行:代碼的裝入、代碼的校驗(yàn)和代碼的執(zhí)行。裝入代碼的工作由"類裝載器"(class loader)完成。類裝載器負(fù)責(zé)裝入運(yùn)行一個(gè)程序需要的所有代碼,這也包括程序代碼中的類所繼承的類和被其調(diào)用的類。當(dāng)類裝載器裝入一個(gè)類時(shí),該類被放在自己的名字空間中。除了通過符號(hào)引用自己名字空間以外的類,類之間沒有其他辦法可以影響其他類。在本臺(tái)計(jì)算機(jī)上的所有類都在同一地址空間內(nèi),而所有從外部引進(jìn)的類,都有一個(gè)自己獨(dú)立的名字空間。這使得本地類通過共享相同的名字空間獲得較高的運(yùn)行效率,同時(shí)又保證它們與從外部引進(jìn)的類不會(huì)相互影響。當(dāng)裝入了運(yùn)行程序需要的所有類后,解釋器便可確定整個(gè)可執(zhí)行程序的內(nèi)存布局。解釋器為符號(hào)引用同特定的地址空間建立對(duì)應(yīng)關(guān)系及查詢表。通過在這一階段確定代碼的內(nèi)存布局,Java很好地解決了由超類改變而使子類崩潰的問題,同時(shí)也防止了代碼對(duì)地址的非法訪問。
隨后,被裝入的代碼由字節(jié)碼校驗(yàn)器進(jìn)行檢查。校驗(yàn)器可發(fā)現(xiàn)操作數(shù)棧溢出,非法數(shù)據(jù)類型轉(zhuǎn)化等多種錯(cuò)誤。通過校驗(yàn)后,代碼便開始執(zhí)行了。
Java字節(jié)碼的執(zhí)行有兩種方式:
1.即時(shí)編譯方式:解釋器先將字節(jié)碼編譯成機(jī)器碼,然后再執(zhí)行該機(jī)器碼。
2.解釋執(zhí)行方式:解釋器通過每次解釋并執(zhí)行一小段代碼來完成Java字節(jié)碼程 序的所有操作。
通常采用的是第二種方法。由于JVM規(guī)格描述具有足夠的靈活性,這使得將字節(jié)碼翻譯為機(jī)器代碼的工作
具有較高的效率。對(duì)于那些對(duì)運(yùn)行速度要求較高的應(yīng)用程序,解釋器可將Java字節(jié)碼即時(shí)編譯為機(jī)器碼,從而很好地保證了Java代碼的可移植性和高性能。
java中負(fù)責(zé)對(duì)字節(jié)代碼解釋執(zhí)行的是虛擬機(jī),虛擬機(jī)將得到的字節(jié)代碼進(jìn)行編碼運(yùn)行。
java程序,是先把java源程序通過javac命令編譯成字節(jié)碼文件,然后再用java命令解釋執(zhí)行。把字節(jié)碼文件復(fù)制到其它計(jì)算機(jī)上,只要有java虛擬機(jī)就可以用java命令來執(zhí)行。