【從 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 記憶體中,然後再例項化為物件,最終被程式使用。