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程序編譯和運(yùn)行的過程
Java整個(gè)編譯以及運(yùn)行的過程相當(dāng)繁瑣,本文通過一個(gè)簡單的程序來簡單的說明整個(gè)流程。
如下圖,Java程序從源文件創(chuàng)建到程序運(yùn)行要經(jīng)過兩大步驟:
1、源文件由編譯器編譯成字節(jié)碼(ByteCode)
2、字節(jié)碼由java虛擬機(jī)解釋運(yùn)行。因?yàn)閖ava程序既要編譯同時(shí)也要經(jīng)過JVM的解釋運(yùn)行,所以說Java被稱為半解釋語言( “semi-interpreted” language)。
下面通過以下這個(gè)java程序,來說明java程序從編譯到最后運(yùn)行的整個(gè)流程。代碼如下:
第一步(編譯): 創(chuàng)建完源文件之后,程序會(huì)先被編譯為.class文件。Java編譯一個(gè)類時(shí),如果這個(gè)類所依賴的類還沒有被編譯,編譯器就會(huì)先編譯這個(gè)被依賴的類,然后引用,否則直接引用,這個(gè)有點(diǎn)象make。如果java編譯器在指定目錄下找不到該類所其依賴的類的.class文件或者.java源文件的話,編譯器話報(bào)“cant find symbol”的錯(cuò)誤。
編譯后的字節(jié)碼文件格式主要分為兩部分:常量池和方法字節(jié)碼。常量池記錄的是代碼出現(xiàn)過的所有token(類名,成員變量名等等)以及符號(hào)引用(方法引用,成員變量引用等等);方法字節(jié)碼放的是類中各個(gè)方法的字節(jié)碼。下面是MainApp.class通過反匯編的結(jié)果,我們可以清楚看到.class文件的結(jié)構(gòu):
第二步(運(yùn)行):java類運(yùn)行的過程大概可分為兩個(gè)過程:1、類的加載 2、類的執(zhí)行。需要說明的是:JVM主要在程序第一次主動(dòng)使用類的時(shí)候,才會(huì)去加載該類。也就是說,JVM并不是在一開始就把一個(gè)程序就所有的類都加載到內(nèi)存中,而是到不得不用的時(shí)候才把它加載進(jìn)來,而且只加載一次。
下面是程序運(yùn)行的詳細(xì)步驟:
1、在編譯好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系統(tǒng)就會(huì)啟動(dòng)一個(gè)jvm進(jìn)程,jvm進(jìn)程從classpath路徑中找到一個(gè)名為AppMain.class的二進(jìn)制文件,將MainApp的類信息加載到運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi),這個(gè)過程叫做MainApp類的加載。
2、然后JVM找到AppMain的主函數(shù)入口,開始執(zhí)行main函數(shù)。
3、main函數(shù)的第一條命令是Animal animal = new Animal(“Puppy”);就是讓JVM創(chuàng)建一個(gè)Animal對(duì)象,但是這時(shí)候方法區(qū)中沒有Animal類的信息,所以JVM馬上加載Animal類,把Animal類的類型信息放到方法區(qū)中。
4、加載完Animal類之后,Java虛擬機(jī)做的第一件事情就是在堆區(qū)中為一個(gè)新的Animal實(shí)例分配內(nèi)存, 然后調(diào)用構(gòu)造函數(shù)初始化Animal實(shí)例,這個(gè)Animal實(shí)例持有著指向方法區(qū)的Animal類的類型信息(其中包含有方法表,java動(dòng)態(tài)綁定的底層實(shí)現(xiàn))的引用。
5、當(dāng)使用animal.printName()的時(shí)候,JVM根據(jù)animal引用找到Animal對(duì)象,然后根據(jù)Animal對(duì)象持有的引用定位到方法區(qū)中Animal類的類型信息的方法表,獲得printName()函數(shù)的字節(jié)碼的地址。
6、開始運(yùn)行printName()函數(shù)。
特別說明:java類中所有public和protected的實(shí)例方法都采用動(dòng)態(tài)綁定機(jī)制,所有私有方法、靜態(tài)方法、構(gòu)造器及初始化方法《clinit》都是采用靜態(tài)綁定機(jī)制。而使用動(dòng)態(tài)綁定機(jī)制的時(shí)候會(huì)用到方法表,靜態(tài)綁定時(shí)并不會(huì)用到。本文只是講述java程序運(yùn)行的大概過程,所以并沒有細(xì)加區(qū)分。
Java程序編譯運(yùn)行程序詳解
1、打開java軟件,然后把點(diǎn)擊上方的菜單欄的“文件”,會(huì)出現(xiàn)一個(gè)菜單,點(diǎn)擊第一個(gè)“新建”,會(huì)出現(xiàn)一個(gè)二級(jí)菜單,然后點(diǎn)擊“java項(xiàng)目”。
2、之后,會(huì)彈出一個(gè)“新建Java項(xiàng)目”頁面。
首先,在“項(xiàng)目名”那里填寫上你的項(xiàng)目的名字。
接著,設(shè)置項(xiàng)目的位置。(要設(shè)置項(xiàng)目的位置的話,你得先吧那個(gè)“使用缺省位置”復(fù)選框去掉,你才能設(shè)置。)
設(shè)置好之后,點(diǎn)擊“完成”。便會(huì)新建出一個(gè)項(xiàng)目。
3、如果沒有彈出頁面,那么你可以點(diǎn)擊在歡迎頁面的右上角的工作臺(tái)。
4、點(diǎn)擊之后會(huì)出現(xiàn)一個(gè)頁面,你可以在頁面的右上角點(diǎn)擊一個(gè)叫“java”的按鈕,接著會(huì)出現(xiàn)一個(gè)新的頁面,如圖所示。
5、找到“包資源管理器”,那里有一個(gè)你剛才創(chuàng)建項(xiàng)目的文件夾,右擊它會(huì)出現(xiàn)一個(gè)菜單,點(diǎn)擊“新建”,會(huì)出現(xiàn)一個(gè)二級(jí)菜單,點(diǎn)擊“類”。
6、接著會(huì)彈出一個(gè)“新建Java類”頁面。
在名稱那里輸入類的名稱。
然后在“Public static void main (String[] args)”復(fù)選框打上勾。
點(diǎn)擊“完成”。
7、之后,在“包資源管理器”那里會(huì)出現(xiàn)一些文件。
頁面中部會(huì)彈出一個(gè)小頁面,那就是程序的編輯器。
8、在程序編輯器的
“public static void main(String[] args) {
}”
這段程序的中括號(hào)里面輸入System.out.println(“hello world!”);
這段程序。
9、然后在菜單欄里找到“運(yùn)行”并點(diǎn)擊,會(huì)跳出一個(gè)二級(jí)菜單,然后點(diǎn)擊運(yùn)行。
10、如果你沒保存的話,他會(huì)跳出一個(gè)如圖所示的界面,點(diǎn)擊“確定”即可。
11、然后頁面下方會(huì)出現(xiàn)程序的運(yùn)行結(jié)果。
評(píng)論