Java編譯原理
Java 虛擬機(JVM)是可運行Java 代碼的假想計算機。只要根據JVM規格描述將解釋器移植到特定的計算機上,就能保證經過編譯的任何Java代碼能夠在該系統上運行。
一、Java源文件的編譯、下載 、解釋和執行
Java應用程序的開發周期包括編譯、下載 、解釋和執行幾個部分。Java編譯程序將Java源程序翻譯為JVM可執行代碼?字節碼。這一編譯過程同C/C++ 的編譯有些不同。當C編譯器編譯生成一個對象的代碼時,該代碼是為在某一特定硬件平臺運行而產生的。因此,在編譯過程中,編譯程序通過查表將所有對符號的引用轉換為特定的內存偏移量,以保證程序運行。Java編譯器卻不將對變量和方法的引用編譯為數值引用,也不確定程序執行過程中的內存布局,而是將這些符號引用信息保留在字節碼中,由解釋器在運行過程中創立內存布局,然后再通過查表來確定一個方法所在的地址。這樣就有效的保證了Java的可移植性和安全 性。
運行JVM字節碼的工作是由解釋器來完成的。解釋執行過程分三部進行:代碼的裝入、代碼的校驗和代碼的執行。裝入代碼的工作由“類裝載器”(class loader)完成。類裝載器負責裝入運行一個程序需要的所有代碼,這也包括程序代碼中的類所繼承的類和被其調用的類。當類裝載器裝入一個類時,該類被放在自己的名字空間中。除了通過符號引用自己名字空間以外的類,類之間沒有其他辦法可以影響其他類。在本臺計算機上的所有類都在同一地址空間內,而所有從外部引進的類,都有一個自己獨立的名字空間。這使得本地類通過共享相同的名字空間獲得較高的運行效率,同時又保證它們與從外部引進的類不會相互影響。當裝入了運行程序需要的所有類后,解釋器便可確定整個可執行程序的內存布局。解釋器為符號引用同特定的地址空間建立對應關系及查詢表。通過在這一階段確定代碼的內存布局,Java很好地解決了由超類改變而使子類崩潰的問題,同時也防止了代碼對地址的非法訪問。
隨后,被裝入的代碼由字節碼校驗器進行檢查。校驗器可發現操作數棧溢出,非法數據類型轉化等多種錯誤。通過校驗后,代碼便開始執行了。
二、Java字節碼的執行有兩種方式:
1、即時編譯方式:解釋器先將字節碼編譯成機器碼,然后再執行該機器碼。
2、解釋執行方式:解釋器通過每次解釋并執行一小段代碼來完成Java字節碼程 序的所有操作。
通常采用的是第二種方法。由于JVM規格描述具有足夠的靈活性,這使得將字節碼翻譯為機器代碼的工作
具有較高的效率。對于那些對運行速度要求較高的應用程序,解釋器可將Java字節碼即時編譯為機器碼,從而很好地保證了Java代碼的可移植性和高性能。
Java程序編譯和運行的過程
Java整個編譯以及運行的過程相當繁瑣,本文通過一個簡單的程序來簡單的說明整個流程。
如下圖,Java程序從源文件創建到程序運行要經過兩大步驟:
1、源文件由編譯器編譯成字節碼(ByteCode)
2、字節碼由java虛擬機解釋運行。因為java程序既要編譯同時也要經過JVM的解釋運行,所以說Java被稱為半解釋語言( “semi-interpreted” language)。
下面通過以下這個java程序,來說明java程序從編譯到最后運行的整個流程。代碼如下:
第一步(編譯): 創建完源文件之后,程序會先被編譯為.class文件。Java編譯一個類時,如果這個類所依賴的類還沒有被編譯,編譯器就會先編譯這個被依賴的類,然后引用,否則直接引用,這個有點象make。如果java編譯器在指定目錄下找不到該類所其依賴的類的.class文件或者.java源文件的話,編譯器話報“cant find symbol”的錯誤。
編譯后的字節碼文件格式主要分為兩部分:常量池和方法字節碼。常量池記錄的是代碼出現過的所有token(類名,成員變量名等等)以及符號引用(方法引用,成員變量引用等等);方法字節碼放的是類中各個方法的字節碼。下面是MainApp.class通過反匯編的結果,我們可以清楚看到.class文件的結構:
第二步(運行):java類運行的過程大概可分為兩個過程:1、類的加載 2、類的執行。需要說明的是:JVM主要在程序第一次主動使用類的時候,才會去加載該類。也就是說,JVM并不是在一開始就把一個程序就所有的類都加載到內存中,而是到不得不用的時候才把它加載進來,而且只加載一次。
下面是程序運行的詳細步驟:
1、在編譯好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系統就會啟動一個jvm進程,jvm進程從classpath路徑中找到一個名為AppMain.class的二進制文件,將MainApp的類信息加載到運行時數據區的方法區內,這個過程叫做MainApp類的加載。
2、然后JVM找到AppMain的主函數入口,開始執行main函數。
3、main函數的第一條命令是Animal animal = new Animal(“Puppy”);就是讓JVM創建一個Animal對象,但是這時候方法區中沒有Animal類的信息,所以JVM馬上加載Animal類,把Animal類的類型信息放到方法區中。
4、加載完Animal類之后,Java虛擬機做的第一件事情就是在堆區中為一個新的Animal實例分配內存, 然后調用構造函數初始化Animal實例,這個Animal實例持有著指向方法區的Animal類的類型信息(其中包含有方法表,java動態綁定的底層實現)的引用。
5、當使用animal.printName()的時候,JVM根據animal引用找到Animal對象,然后根據Animal對象持有的引用定位到方法區中Animal類的類型信息的方法表,獲得printName()函數的字節碼的地址。
6、開始運行printName()函數。
特別說明:java類中所有public和protected的實例方法都采用動態綁定機制,所有私有方法、靜態方法、構造器及初始化方法《clinit》都是采用靜態綁定機制。而使用動態綁定機制的時候會用到方法表,靜態綁定時并不會用到。本文只是講述java程序運行的大概過程,所以并沒有細加區分。
Java程序編譯運行程序詳解
1、打開java軟件,然后把點擊上方的菜單欄的“文件”,會出現一個菜單,點擊第一個“新建”,會出現一個二級菜單,然后點擊“java項目”。
2、之后,會彈出一個“新建Java項目”頁面。
首先,在“項目名”那里填寫上你的項目的名字。
接著,設置項目的位置。(要設置項目的位置的話,你得先吧那個“使用缺省位置”復選框去掉,你才能設置。)
設置好之后,點擊“完成”。便會新建出一個項目。
3、如果沒有彈出頁面,那么你可以點擊在歡迎頁面的右上角的工作臺。
4、點擊之后會出現一個頁面,你可以在頁面的右上角點擊一個叫“java”的按鈕,接著會出現一個新的頁面,如圖所示。
5、找到“包資源管理器”,那里有一個你剛才創建項目的文件夾,右擊它會出現一個菜單,點擊“新建”,會出現一個二級菜單,點擊“類”。
6、接著會彈出一個“新建Java類”頁面。
在名稱那里輸入類的名稱。
然后在“Public static void main (String[] args)”復選框打上勾。
點擊“完成”。
7、之后,在“包資源管理器”那里會出現一些文件。
頁面中部會彈出一個小頁面,那就是程序的編輯器。
8、在程序編輯器的
“public static void main(String[] args) {
}”
這段程序的中括號里面輸入System.out.println(“hello world!”);
這段程序。
9、然后在菜單欄里找到“運行”并點擊,會跳出一個二級菜單,然后點擊運行。
10、如果你沒保存的話,他會跳出一個如圖所示的界面,點擊“確定”即可。
11、然后頁面下方會出現程序的運行結果。
評論