核心問題
JDK 源碼包含大量 Java 代碼(如集合框架、編譯器自身),編譯 JDK 需要 JDK,這是經典的「雞生蛋、蛋生雞」循環依賴。第一個 JDK 究竟怎麼編譯出來的?
歷史真相:首個 JDK 由 C/C++ 編寫
- Java 1.0 (1996) 的核心運行時、編譯器、標準庫主要由 C/C++ 實現
- Sun 團隊用 C 寫了第一個 Java 編譯器 (javac) 和 JVM
- 後續版本逐步實現「自宿主」:用 Java 重寫編譯器,用舊版 JDK 編譯新版 JDK
通用語言啟動三大策略
策略一:滾雪球遞歸構建
- 用機器碼/匯編寫最小子集編譯器(只支持 goto、if、基本運算)
- 用這個子集寫功能更豐富的編譯器 v2
- 迭代至完整語言特性
- 代表:C 語言早期發展、Go 語言早期
策略二:人肉編譯
- 用目標語言寫編譯器源碼
- 人工將源碼翻譯為機器碼(極其痛苦)
- 得到第一個可運行編譯器
- 代表:早期 Pascal、某些教學型語言
策略三:交叉編譯
- 用成熟語言(C/C++/Rust)寫目標語言編譯器
- 在宿主平台編譯得到目標平台可執行文件
- 代表:JDK(C++ 寫 javac)、Python (C 實現 CPython)、V8 (C++ 實現 JS 引擎)
現代 JDK 構建流程
Bootstrap JDK (N-1 版本)
↓ 編譯
OpenJDK 源碼
↓
新版 JDK (N 版本)
↓ 可作為下一版 Bootstrap JDK
- 構建 JDK N 需要 JDK N-1 作為 Bootstrap JDK
- OpenJDK 官方提供預構建的 Bootstrap JDK 下載
- 支持「無頭構建」:只構建工具鏈,不構建完整 JDK
有趣的遞歸現象
// Java 編譯器用 Java 寫
public class JavaCompiler {
public void compile(String source) { ... }
}
// 但運行在 JVM 上
// JVM 用 C++ 寫 (HotSpot)
// JVM 運行在操作系統上
// 操作系統用 C/汇編寫
// 處理器執行機器指令
// 指令由邏輯門電路實現
// 電路由物理定律支配
總結
第一個 JDK 是用 C/C++ 寫的,打破了循環依賴。現代語言開發普遍採用交叉編譯策略:用成熟工具鏈構建新工具鏈。這也是為什麼編譯器開發常說「編譯器是編譯器自己的最佳測試用例」——因為最終都要實現自宿主。