Java語言的「編譯期」是一段不肯定的過程,由於它可能指的是前端編譯器把java文件轉變成class字節碼文件的過程,也可能指的是虛擬機後端運行期間編譯器(JIT)把字節碼轉變成機器碼的過程。
下面討論的編譯期優化指的是javac編譯器將java文件轉化爲字節碼的過程,而運行期間優化指的是JIT編譯器所作的優化。
編譯期優化
虛擬機設計團隊把對性能的優化集中到了後端的即時編譯器(JIT)中,這樣可讓那些不是由javac編譯器產生的class文件也一樣能享受到編譯器優化所帶來的好處。可是javac作了許多針對編碼過程的優化措施來改善程序員的編碼風格和提升編碼效率。許多新生的java語法特性,都是靠編譯器的「語法糖」來實現,而不是依賴虛擬機的底層改進來支持。
因此說,java中即時編譯器在運行期間的優化過程對於程序的運行來講更重要,而前端編譯器在編譯期的優化過程對於程序編碼來講關係更加密切。
javac編譯器的編譯過程大體可分爲三個步驟:
1.解析與填充符號表過程;
2.插入式註解處理器的註解處理過程;
3.語義分析與字節碼生成過程。
下面分別來介紹。
解析與填充符號表;
解析步驟包含了
詞法分析和語法分析兩個過程,首先詞法分析是將源代碼的字符流轉變成爲標記集合(token),而後語法分析是根據token序列來構造抽象語法樹(一種用來描述程序代碼語法結構的樹狀表示方式)。完成詞法分析和語法分析以後,下一步是
填充符號表,符號表是由一組符號地址和符號信息構成的表格,符號表中所登記的信息在編譯的不一樣階段都要用到(好比語義分析中符號表所登記的內容將用於語義檢查和產生中間代碼,目標代碼生成階段當對符號名進行地址分配時,符號表是地址分配的依據)。
插入式註解處理器的註解處理過程:
插入式註解處理器能夠看作是一組編譯器的插件,在這些插件裏面,能夠讀取、修改、添加抽象語法樹中的任意元素。若是這些插件在處理註解期間對語法樹進行了修改,那麼編譯器將
回到解析及填充符號表的過程從新處理,直到全部的插入式註解處理器都沒有再對語法樹進行修改成止。
語義分析與字節碼生成過程:
語法分析以後,編譯器得到了程序代碼的抽象語法樹表示,語法樹可以表示結構正確的源程序的抽象,可是沒法保證源程序是否符合邏輯,而語義分析主要是對結構上正確的源程序進行上下文有關性質的檢查。
1.標註檢查
標註檢查步驟檢查的內容包括諸如變量使用前是否已被聲明、變量與賦值之間的數據類型是否可以匹配,等等。還有一個重要的動做稱爲常量摺疊也在此階段完成。
2.數據及控制流分析
數據及控制流分析是對程序上下文邏輯更進一步的驗證,它能夠檢查出諸如程序局部變量在使用前是否有賦值、方法的每條路徑是否有返回值、是否全部的受查異常都被正確處理了等問題。
3.解語法糖
語法糖是指在計算機語言中添加某種語法,這種語法對語言的功能並無影響,可是更方便程序員使用。java中的泛型,變長參數,自動拆箱與裝箱,條件編譯等就屬於語法糖,它們在編譯階段就被還原成簡單的語法結構(好比List<String>和List<Integer>在運行期間實際上是同一個類)。
4.字節碼生成
此過程是javac編譯過程的最後一個階段,字節碼生成階段將以前各個步驟所生成的信息轉化成字節碼寫到磁盤中,另外還進行少許的代碼添加和轉換工做。
運行期優化
在部分商用虛擬機中,java程序最初是經過解釋器進行解釋執行的,當虛擬機發現某個方法或代碼塊運行特別頻繁,就會把這些代碼認定爲「熱點代碼」,爲了提升熱點代碼的執行效率,在運行時,虛擬機就會把這些代碼編譯成與本地平臺
相關的機器碼,並進行各類層次的優化,完成這個任務的編譯器稱爲即時編譯器或JIT編譯器。
即時編譯器並非虛擬機必須的部分,可是即時編譯器編譯性能的好壞、代碼優化程度的高低確是衡量一款商用虛擬機優秀與否的最關鍵的指標之一。
衆多主流的虛擬機都同時包含解釋器和JIT編譯器,解釋器與JIT編譯器各有優點:當程序須要迅速啓動和執行時,解釋器能夠首先發揮做用,省去編譯的時間,當即執行。當程序運行後,隨着事件的推移,JIT編譯器逐漸發揮做用,把愈來愈多的代碼編譯成本地代碼以後,能夠獲取更高的執行效率。
會被即時編譯器編譯的熱點代碼有兩類:
1.被屢次調用的方法體;
2.被屢次調用的循環體。
即時編譯器會以整個方法做爲編譯對象,將其編譯成機器碼。這種編譯方式由於編譯發生在方法執行過程之中,所以被稱做棧上替換(OSR)。
判斷一段代碼是不是熱點代碼的方式(熱點探測)有兩種:
1.基於採樣的熱點探測:
此方法會週期性檢查各個線程的棧頂,若是發現某個或某些方法常常出如今棧頂,那麼這個方法就是熱點方法。此方法的缺點是很難精確地確認一個方法的熱度,容易受到諸如線程阻塞等因素影響。
2.基於計數器的熱點探測:
此方法會爲每一個方法甚至是代碼塊創建計數器,統計方法的執行次數,若是執行次數超過一個閥值就認爲它是熱點方法。
注:默認設置下,執行引擎並不會同步等待編譯請求完成,而是繼續進入解釋器按照解釋方式執行字節碼,直到提交的請求被編譯器編譯完成。當編譯工做完成以後,這個方法的調用入口地址就會被系統自動改寫成新的地址,下一次調用該方法時就會使用已編譯的版本。也就是說,在編譯器還未完成以前,執行引擎仍按照解釋方式繼續執行,而編譯動做則在後臺的編譯線程中進行。
優化技術:
通常來講即時編譯器所產生的本地代碼會比javac產生的字節碼更優秀。即時編譯器採用了一系列的技術來優化代碼,好比公共子表達式消除,數組範圍內檢查消除,方法內聯,逃逸分析等。