【從 1 開始學 JVM 系列】
JVM 對於每位 Java 語言程式設計者來說無疑是“重中之重”,儘管我們每天都在與它打交道,卻很少來審視它、瞭解它,慢慢地,它成為了我們“熟悉的陌生人”。
因此,我計劃寫一個「從 1 開始學 JVM 系列」 ,主要面向有一定 Java 基礎的同學。同時,梳理總結一下自己過去積累的 JVM 體系知識和技能。
從 JVM 基礎知識聊起
常見的程式語言是如何分類的?
眾多周知,Java 是一門面向物件的程式語言。
對於程式語言,使用不同的標準有不同的分類,我們不妨一起來看看常見的分類。
第一種常見的分類為面向過程、面向物件、面向函式的程式語言。
- 面向過程,如 C
- 面向物件,如 Java、C++
- 面向函式,如 Scala
第二種可以將程式語言分為靜態型別、動態型別。
- 靜態型別,如 Java
- 動態型別,如 python、javascript
第三種可以將程式語言分為有虛擬機器、無虛擬機器。
- 有虛擬機器,如 Java
- 無虛擬機器,如 C、C++
第四種可以將程式語言分為有 GC、無 GC。
有 GC,如 Java、Go
無 GC,如 C、C++
對於沒有 GC 的程式語言人工管理容易出現記憶體洩漏和野指標,例如 C++,這就要求程式設計者要足夠細心。
通過對前面分類的小結,我們知道,Java 是一種面向物件、靜態型別、有虛擬機器、有 GC 的高階語言。
此外,Java 同時支援編譯執行和解釋執行、有執行時、能夠跨平臺(Write once, run anywhere,即“一次編寫,到處執行”)。
- 即時編譯執行,將一個方法中包含的所有位元組碼編譯成機器碼後再執行
- 解釋執行,即逐條將位元組碼翻譯成機器碼並執行。
Java 程式碼解釋執行,到達一定的次數後,如果被判定為是熱點程式碼,則會被編譯成機器碼執行(一般執行效率會更高)。
程式語言如何跨平臺?
一般而言,有兩種跨平臺的方式。
第一種方式是「原始碼跨平臺」。
這種方式通過在不同的平臺上(例如分別在 Linux、Window)編譯原始碼,生成不同的二進位制檔案,從而獲得跨平臺執行的能力。
但缺點也很明顯,特定平臺上編譯出來的二進位制無法跨平臺執行。
如 Linux 編譯出來的二進位制檔案無法在 Windows 上執行。
第二種方式是「二進位制跨平臺」。
例如 Java 語言,通過講原始碼編譯成位元組碼,從而就能夠實現跨平臺執行。
為什麼二進位制能夠跨平臺?
一個非常重要的原因是虛擬機器的誕生,使得在不同的平臺上都能執行相同的位元組碼檔案。
Java、C++、Rust 有哪些區別?
我們以幾種常見的程式語言為例,對比一下不同型別的程式語言,看看它們之間的區別。
語言 | 對程式設計師態度 | 優勢 | 劣勢 |
---|---|---|---|
C/C++ | 完全相信、慣著程式設計師 | 自行管理記憶體,程式碼編寫很自由 | 不小心會造成記憶體洩漏等問題,導致程式崩潰 |
Java/Golang | 完全不相信、但慣著程式設計師 | 記憶體生命週期都由 JVM 執行時統一管理。絕大部分場景,非常自由的寫程式碼,不用關心記憶體情況;記憶體使用有問題時,可以通過 JVM 資訊進行分析診斷和調整 | 存在 STW,無法靈活管理記憶體 |
Rust | 既不相信程式設計師,也不慣著程式設計師 | 寫程式碼時,必須清楚用 Rust 的規則管理好變數,好讓機器能明白高效地分析和管理記憶體 | 程式碼不利於人的理解,寫程式碼很不自由,學習成本也很高 |
位元組碼、類載入器、虛擬機器之間是什麼關係?
我們通過對照一張圖來說明它們之間的關係。
Java 原始碼被編譯成「位元組碼檔案」(即 xxx.class 檔案),然後通過「類載入器(ClassLoader)」將位元組碼檔案載入到 JVM 記憶體中,然後再例項化為物件,最終被程式使用。