Java 不僅僅是一種程式語言,更是一個「大傘」,涵蓋了工具、執行環境,甚至是一個完整的社群。在本文中,我們想要探討這個傘下的一部分: Java 虛擬機(JVM)。 JVM 到底是什麼?以及它是如何運作的?
Java在最受歡迎的程式語言排行榜上一直名列前茅。真正的原因不是語言本身,而是JVM以及它為開發者提供的強大能力。-- Simon Ritter, Deputy CTO, Azul
軟體產業有許多的術語以及縮寫,為了避免困惑,導致對特定術語的誤用,我們首先需要理解以下術語縮寫的差異。
JDK 位於 Java 生態系統的最高層,是所有其他組件的「來源」。每個新的 Java 版本都是 JDK 的新版本,其中包含改進、錯誤修正、安全修補以及新增或移除的工具。 JDK 包含數個組件:
一套工具可協助您建立、監控和執行 Java 應用程式,例如:
• javac:用於將您的 Java 類別編譯為 Bytecode 。
• javadoc:從 Java 原始碼文件生成 API 文件的 HTML 頁面。
• jshell:在 Shell 終端中與執行 Java 程式碼互動並執行它。
• 還有許多其他工具...
在 Java 9 之前,每個 JDK 都有相應的 JRE,它包含相同的 JVM 和函式庫,但僅提供有限的工具集。因此,它是 JDK 的一個子集。 JRE 的目標是在不需要所有工具的設備上執行應用程式,以減少開銷,並提供更小的下載和分發文件。然而,從 Java 9 開始,引入了模組(modules)的概念,使得您可以使用 jlink 建立只包含運行應用程式所需模組的執行環境,從而相對於 JDK 和 JRE 而言,創建更小的執行時環境。
由於許多組織仍然依賴並使用 JRE,Azul 和其他提供者仍然針對新版本創建 JRE。請查看 Azul Core 的下載頁面,以獲取所有可用 JRE 的列表。
正如我們所學到的, Java 虛擬機(JVM)是 JDK 的一部分,它運行我們的應用程式,並被稱為「受管理的執行時環境」。這個術語中的「受管理的」非常重要,因為表示它不僅執行程式碼,還處理了許多額外的功能。 JVM 充當您的應用程式與運行的機器之間的中介層。
作為開發人員,您只需要編寫一個代碼庫,就可以在任何平台上執行您的程式,包括操作系統(如Windows、Mac、Linux等)和硬體(如x86、ARM、aarch64等)。為了實現這一點,您的Java代碼需要轉換為 Bytecode 。這是您作為開發人員的角色,可以通過像 Maven 或 Gradle 這樣的建構工具使用 javac 來完成。JVM 在機器上執行這些 Bytecode ,這意味著任何類別都可以在任何平台上運行!
為了實現這一點,JVM Bytecode 必須對每個支援的操作系統和平台進行「翻譯」,轉換成適用該環境的特定本機碼(native code )。這意味著作為開發人員,您不需要自己為支援不同平台而撰寫不同的程式碼,因為這些工作已經由 Java 系統的開發人員處理。您可以查看原始程式碼,了解在 Java 項目中是如何完成這些工作的,所有這些代碼在 GitHub 上都是免費可用的。連結上,您可以在以下項目的一些截圖中找到 HotSpot 虛擬機的不同實現方式。
當您的應用程式啟動時, Bytecode 會使用 JVM 中的範本模型直接轉換為平台指令。此時,還沒有進行任何優化,因此 Java 運行速度比類似的本機編譯程式碼要慢。但與此同時, JVM 立即追蹤每個方法被調用的頻率。一旦達到預定的閾值(也稱為 HotSpot ), JVM 內部的即時編譯器( JIT 編譯器)開始進行兩個階段的工作:
C1 JIT:將程式碼第一次轉換為本機碼。
• 一旦檢測到某個方法作為 Hotspot,該程式碼將重新編譯為本機碼。
• 這個過程盡可能地快速進行,最小化優化,並且在使用此本機碼時, JVM 再次進行分析,以了解它的使用方式。
觀察下方圖表,我們可以看到當從黃色的 Bytecode 轉換為第一次最佳化的程式碼(C1,綠色)時,應用程式的速度如何改善,最終在完成最佳化程式碼(C2,藍色)下達到最佳性能。有關這個主題的更多資訊以及如何「調整」應用程式以改善這些不同步驟的方法,請參閱「分析和調整程式預熱」。
JIT(即時編譯)是與 AOT(事先編譯)相對的概念,通常在生成的本機碼針對其確切需求進行優化時表現更好。
如果您想了解更多有關將 Bytecode 轉換為本機碼的過程,可以閱讀 Simon Ritter 的這些部落格文章:
JVM 的另一個責任是自動化的記憶體管理。在其他程式語言如 C、C++ 中,作為開發人員,您需要分配(malloc)和釋放記憶體,如果不正確處理,可能會產生記憶體問題。垃圾收集器(GC)是 JVM 中負責處理這一部分的組件。它完全解放了您作為開發人員對於管理記憶體使用的擔憂。GC 定期查找不再需要並且在程式碼中未被引用的物件,釋放空間以供重新使用。
您可以在這裡找到關於垃圾收集器的完整文章:作為 Java 開發人員,我應該了解垃圾收集嗎?
管理線程和線程之間的交互也是由 JVM 處理的一個主題。
OpenJDK 19 中包含了項目 Loom 和虛擬線程的首個評估版本,介紹了 Java 內部的多層級線程,與操作系統線程無關。這將為 Java 中的線程使用方式創造新的動態。與操作系統線程的數量無關使得效率更高,並且對可擴展性、流式處理或消息應用程序將產生重大影響。
這裡提供更多資訊:JDK 19 和 Java 用戶應該了解的內容。
另一個常聽到的與 Java 相關的術語是它是一種「靜態型語言」。這裡的「靜態」指的是變數的使用方式。當你宣告一個新的值時,你需要定義一個型別,並且不能在之後改變它。例如,String label = "Hello, World!" 將不允許你之後將一個數字指派給 label 變數。這點和 JavaScript 等語言不同。對於從動態型別語言轉換到 Java 的新手來說,這可能會令人困惑,但這有助於在程式碼執行時避免許多問題和錯誤。
請記住,「靜態」這個詞彙與值的定義方式相關聯,因為相反地,類別在執行時以「動態」方式載入!這使得應用程式可以根據環境或特定設定正確地運作。例如,根據環境設定,在測試時與在正式環境中可以使用不同的資料庫。在每個情況下,其他類別可以動態載入以與資料庫互動。
所有的 Java 運行時都必須以相同的方式運作,這樣作為使用者,您可以確保您的應用程式無論使用哪個 JDK,都會產生相同的結果。為了實現這一點,每個發行版都必須符合由相關的 Java 規範請求(JSR)通過 Java 社群流程(JCP)定義的 Java SE 規範。
為了驗證這一點,發行版必須通過 Java 技術相容性套件(TCK)的所有測試。這並不意味著所有的發行版都是以相同的方式實現的,儘管大多數都基於 OpenJDK!Azul 提供了兩個符合 TCK 的發行版,但它們在很多方面都有很大的差異。
這個發行版基於OpenJDK。
但有一些功能上的更改:
所有OpenJDK的垃圾收集器都被移除並替換為Azul C4垃圾收集器。
C2 JIT 編譯器沒有被移除,但添加了Azul Falcon編譯器,可以通過啟動選項選擇使用哪個編譯器。
在JDK內添加了額外的功能:
• Connected Runtime Service (CRS) 可以連接到 Azul Vulnerability Detection,幫助您實時識別安全問題。
• ReadyNow!:一種技術,使Java應用程序能夠快速啟動並保持高速運行。
提供方式為 Azul Platform Prime:
• Azul Prime Builds of OpenJDK + C4 + Falcon + ReadyNow! + 支援 + 升級 + 額外工具。
• 提供免費評估和開發版本以及授權版本。
Azul JDK發行版可從 “ Download Azul JDKs ” 下載。
使用 Azul Platform Prime 可讓下述技術或服務時,降低基礎設施成本並提升效能。
Cassandra:提升承載能力,消除記憶體瓶頸,並將基礎設施的使用量減少高達50%。
Kafka:在運行Kafka時,至少減少20%基礎設施使用量,同時提升30%的運行速度,增加吞吐量,並確保應用程式在處理不同負載時能夠穩定運行,不會出現劇烈的性能變化或延遲。
Solr:使用少於40% 基礎設施使用量的情況下,實現更高的吞吐量,並減少應用程式的暫停時間。
Elasticsearch:不需修改應用程式碼,將Elasticsearch的回應時間從第75至99個百分位數減少超過75%。
Hadoop和HBase:擴展NameNodes以處理更多HDFS元數據,建立更大的HDFS叢集,同時降低基礎設施成本和操作複雜度。
Spark:對大多數Spark查詢提升24%至100%以上的回應時間,提供更加一致的表現。
Ignite:消除Ignite中的暫停,並在毫秒級別下處理數千個交易,同時擴展至Petabyte級別的數據。
Hazelcast:在Hazelcast中提供更精準的回應時間服務水準協議(SLA),包括在小堆大小下處理500MB至1.5GB的數據。
Tomcat:提升Web伺服器的回應能力,減少暫停時間,同時節省基礎設施資源。
JBoss:消除應用程式的抖動、回應時間不穩定和資料限制,包括JBoss Data Grid、EAP 和中間件應用程式。
Apache ZooKeeper:提升敏感的ZooKeeper管理的一致性和可靠性,包括Kafka事件流基礎設施。
JVM(Java虛擬機)幫助開發人員處理了許多與其他語言相比不需要自己擔心的任務。這讓開發人員能夠更專注於業務邏輯和具體的實作工作。 JVM 負責執行 Java 程式碼、自動管理,處理線程管理,並提供跨平台的環境。透過將這些任務交給 JVM ,開發人員可以專注於撰寫解決特定問題並滿足業務需求的程式碼。簡單來說, JVM 就像是一位幫助開發人員處理瑣碎事務的助手,讓開發人員能夠更有效率地進行開發工作。