1 編譯器分類 Java的編譯過程:將源代碼轉化成機器可執行的二進位代碼。實際上,編譯過程,是分階段進行的,由此產生了不同的編譯器。 編譯器分類: | 類別 | 工作內容 | 代表 | | | | | | 前端編譯器 | 把源代碼轉變成位元組碼 | JDK的Javac、Eclipse編譯器(ECJ) ...
目錄
1 編譯器分類
Java的編譯過程:將源代碼轉化成機器可執行的二進位代碼。實際上,編譯過程,是分階段進行的,由此產生了不同的編譯器。
編譯器分類:
類別 | 工作內容 | 代表 |
---|---|---|
前端編譯器 | 把源代碼轉變成位元組碼 | JDK的Javac、Eclipse編譯器(ECJ) |
即時編譯器 | 運行時把位元組碼轉變成本地機器碼 | HotSpot虛擬機的C1、C2編譯器 |
提前編譯器 | 直接把源代碼編譯成與目標機器指令集相關的二進位代碼 | JDK的Jaotc等 |
2 Javac編譯器
2.1 Javac簡介
- Javac是程式猿使用最多的一款編譯器,但面向IDE編程使得我們很少直接使用javac,開發工具幫我們自動編譯了
- 它由Java編寫
IntelliJ IDEA ,支持幾種編譯器:Javac、Eclipse ECJ、Ajc 等,預設使用Javac
2.2 Javac與程式開發
編譯器如何跟程式員打交道?
- 前端編譯器對程式效率提升影響極少。虛擬機設計團隊將性能優化放在即時編譯器中,讓那些不是由Javac產生的Class文件(如JRuby、Groovy等語言的Class文件)也能被編譯器優化。
- 前端編譯器對程式員開發效率提升影響極大:編譯器的“語法糖”:泛型、自動拆箱、自動裝箱、枚舉類、Lambda表達式等特征,簡化編碼。
3 Javac工作過程
Javac工作過程就是源代碼變為位元組碼的過程。
3.1 解析與填充符號表
3.1.1 詞法分析
- 將源代碼的字元流轉變為標記集合(Token)。
- 單個字元是編碼的最小元素,標記是編譯時的最小元素。關鍵字、變數名、字面量、運算符都可以作為標記
- 因此:詞法分析就是將源代碼拆解關鍵字 。源代碼->關鍵詞集合
int a = 100 這句代碼包含4個標記,分別是int、a、=、100,雖然關鍵字int由3個字元構成,但是它是一個獨立的標記,不可拆分
3.1.2 語法分析
- 根據標記序列構造抽象語法樹(AST)
- 每一個節點代表代碼中的一個語法結構,例如包、類型、修飾符、運算符、介面、返回值等
- 源代碼->關鍵詞集合->語法樹
註意:生成語法樹以後,編譯器後續的操作都是基於語法樹,不再操作源碼
AST View 插件生成抽象語法樹:
上圖看著複雜,換個圖
ps:我沒分析javac源代碼,借用網圖:java編譯器源碼解析-語法分析(1)
3.1.3 填充符號表
先理解符號概念,參考:java編譯器源碼解析-語義分析-填充符號表
一、 符號是什麼?
- java聲明一個類,類中有屬性和方法,電腦識別為符號;
- 符號有名稱,如:類名、方法名、屬性名;
- 符號有類型,如:int a=0;a是一個變數,但編譯器認為a是一個VarSymbol,它的類型是JCPrimitiveType
二、符號表什麼時候生成? 生成語法樹之後
三、符號地址代表什麼? 記憶體地址,在目標代碼生成階段,會對符號名進行地址分配
四、符號內容有什麼? 地址、內容
五、填充前後有什麼變化? 在語法樹的基礎上進一步完善信息
填充符號表
- 生成一組符號地址和符號信息構成的數據結構(類比哈希表中鍵值對)
3.2 註解處理
插入式註解處理器,編譯期間處理註解,讀取,修改,刪除語法樹中的任意元素,編譯器會根據修改與否,重新回到解析及符號表填充階段進行處理。
Lombok:基於插入式註解處理器實現的插件,修改語法樹元素
CheckStyle、FindBug、Klocwork:遍歷和分析語法樹,分析代碼質量
3.3 語義分析
對語法樹進行邏輯驗證
3.3.1 標註檢查
變數是否先聲明後使用、變數類型與值對否匹配
常量摺疊:“a=1+2”優化為“a=3”
3.3.2 數據及控制流分析
檢驗:局部變數先賦值後使用、方法的每條路徑是否都有返回值
跟類載入時的校驗過程類似
3.3.3 解語法糖
java虛擬機不支持泛型、裝箱、拆箱、變長參數等語法
解語法糖:編譯階段還原到基礎語法結構
3.4 生成位元組碼
將語法樹、符號表轉化成位元組碼指令,生成.class文件
將實例構造器
4 總結
Javac編譯過程各節點及說明:
Javac編譯過程中的主體代碼及其功能: