Java為啥能跨平臺(原因解析)JDK/JRE/JVM的關系說明
Java為啥能跨平臺?JDK/JRE/JVM的關系?
JVM到底是個啥?Java為啥能跨平臺、一次編譯處處運行?JDK、JRE、JVM三者到底啥關系?JVM又是怎么把我們寫的代碼跑起來的?
一、JVM是什么
JVM 全稱 Java Virtual Machine(Java虛擬機),本質就是Java程序的運行環(huán)境——它不直接和硬件打交道,而是介于Java代碼和操作系統(tǒng)之間的“中間層”。
我們寫的 .java 源代碼,經過編譯器(javac)編譯后,會變成 .class 文件——這是Java的二進制字節(jié)碼文件(不是操作系統(tǒng)能直接執(zhí)行的機器指令)。而JVM的核心作用,就是讀取并執(zhí)行這份 .class 文件,讓代碼真正跑起來。
二、Java 的跨平臺特性:一次編譯,處處運行
這是Java最核心的優(yōu)勢之一,而背后的關鍵,就是JVM的“適配性設計”——不同操作系統(tǒng)有不同版本的JVM,但它們都能運行同一份 .class 文件。
具體邏輯很簡單:
- 你寫的Java代碼,只需要編譯一次,就能生成統(tǒng)一格式的
.class字節(jié)碼文件(不管你是在Windows、Mac還是Linux上編譯,生成的.class文件完全一樣); - Windows、Mac、Linux等不同操作系統(tǒng),都會提供對應的JVM版本(比如Windows版JVM、Mac版JVM);
- 當你在某個操作系統(tǒng)上運行Java程序時,對應的JVM會把
.class字節(jié)碼,“翻譯”成該操作系統(tǒng)能直接執(zhí)行的機器指令; - 操作系統(tǒng)執(zhí)行這些機器指令,最終完成程序運行。
舉個例子:你在Windows上編譯好的 .class 文件,復制到Mac上,Mac版JVM會自動把它翻譯成Mac系統(tǒng)的指令,無需重新編譯——這就是“一次編譯,處處運行”的本質。
不過要提一句:Docker出現(xiàn)后,Java的跨平臺優(yōu)勢被削弱了(Docker本身就是“容器化跨平臺”),但JVM作為Java軟件的核心運行環(huán)境,依然是不可或缺的。
三、JDK、JRE、JVM:三者的關系
很多人會把這三個概念搞混,其實它們是“包含與被包含”的關系,用一句話就能概括:JDK > JRE > JVM。

1. JVM(Java Virtual Machine)
就是我們前面說的運行 .class 文件的虛擬機——只負責運行 .class 字節(jié)碼文件,是Java程序能跑起來的基礎。但它光自己不行,還需要依賴類庫才能工作。
2. JRE(Java Runtime Environment)
JRE = JVM + Java核心類庫(比如 java.lang.String、java.util.ArrayList 這些基礎類)。
因為代碼執(zhí)行需調用很多 Java SE 的類庫(比如字符串處理、集合操作),所以 JRE 在 JVM 的基礎上自帶了這些類庫。
3. JDK(Java Development Kit)
但我們不是一上來就寫 .class 文件的,我們寫的是 .java 文件,而 .java 文件需要編譯成 .class 文件。該編譯工作由 JDK 提供的工具完成 ——JDK 其實就是 JRE 加上編譯、調試、打包等各種開發(fā)工具的集合。
JDK = JRE + 編譯、調試、打包等開發(fā)工具(比如javac編譯器、java運行命令、jar打包工具、jdb調試軟件等)。
我們寫Java代碼時,不是一上來就寫 .class 文件的,我們寫的是 .java 文件,而 .java 文件需要用 javac 命令編譯 .java 文件成 .class 文件,用 jar 命令打包程序——這個編譯工作由 JDK 提供的工具完成 ,JDK 其實就是 JRE 加上編譯、調試、打包等各種開發(fā)工具的集合。
四、JVM運行代碼流程
JVM運行 .class 字節(jié)碼的過程,核心依賴5個核心組件:類加載系統(tǒng)、運行時數據區(qū)、執(zhí)行引擎、本地方法接口(JNI)、本地方法庫。整個流程就像“工廠加工產品”,一步步把字節(jié)碼變成可執(zhí)行的指令。

1. 第一步:類加載框架
.class 文件里包含了類的所有信息(類名、父類、接口、字段、方法、訪問修飾符等),類加載系統(tǒng)的作用,就是把這些 .class 文件加載到JVM的“內存區(qū)域”(運行時數據區(qū))。
2. 第二步:運行時數據區(qū)
加載后的類、代碼執(zhí)行時產生的數據(比如對象、變量、方法調用信息),都會存放在這里。它相當于JVM的“工作內存”,主要分為5個部分:
| 內存區(qū)域 | 核心作用 |
|---|---|
| 方法區(qū)(Method Area) | 存放已加載類的元數據(類名、父類、接口、字段、方法等),比如你定義的 User 類信息就存在這 |
| 堆(Heap) | 存放代碼中 new 出來的對象(比如 User user = new User()),是JVM中最大的內存區(qū)域 |
| 虛擬機棧(VM Stack) | 存放方法調用的信息(比如調用 main() 方法、add() 方法時,會創(chuàng)建“棧幀”存在這里),方法執(zhí)行完棧幀就銷毀 |
| 程序計數器(Program Counter Register) | 記錄當前線程執(zhí)行到哪一行字節(jié)碼指令(多線程時,切換線程后能恢復執(zhí)行位置,避免亂序) |
| 本地方法棧(Native Method Stack) | 存放Java調用的“本地方法”(用C/C++寫的方法,比如Java底層的IO操作、線程管理)的調用信息 |
3. 第三步:執(zhí)行引擎
.class 文件里的字節(jié)碼,只是JVM能識別的“指令集規(guī)范”,不是操作系統(tǒng)能直接執(zhí)行的機器指令——這時候就需要執(zhí)行引擎來“翻譯”。
執(zhí)行引擎的核心工作:
- 讀取運行時數據區(qū)里的字節(jié)碼指令;
- 把字節(jié)碼指令“解釋”(或編譯)成當前操作系統(tǒng)能識別的機器指令;
- 把機器指令交給CPU執(zhí)行。
這里要提一句:Java不是純“解釋執(zhí)行”,還會用到JIT(Just-In-Time 即時編譯器)——熱點代碼(頻繁執(zhí)行的代碼)會被JIT編譯成機器指令緩存起來,下次執(zhí)行直接用,不用重復解釋,這樣能提升性能。Java 編譯器把 .java 文件編譯為字節(jié)碼文件后,JVM 執(zhí)行引擎將字節(jié)碼指令解釋為對應操作系統(tǒng)能運行的指令并執(zhí)行,這也就是為什么說 Java 是編譯和解釋共存的語言。
4. 第四步:本地方法接口(JNI)+ 本地方法庫
用C/C++寫的“本地方法”,存放在本地方法庫中。就是Java代碼執(zhí)行時,會遇到一些“Java做不了”的操作(比如操控硬件、底層IO、線程調度),這些操作都
前文也提到了,本地辦法執(zhí)行時,相關信息會放到運行時數據區(qū)的本地方法棧里。
到此這篇關于java多線程的文章就介紹到這了,更多相關java多線程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
springboot自動掃描添加的BeanDefinition源碼實例詳解
這篇文章主要給大家介紹了關于springboot自動掃描添加的BeanDefinition的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-02-02
Springboot+Mybatis實現(xiàn)分頁加條件查詢功能
這篇文章主要為大家詳細介紹了Springboot+Mybatis實現(xiàn)分頁加條件查詢,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04
解決Spring Cloud feign GET請求無法用實體傳參的問題
這篇文章主要介紹了解決Spring Cloud feign GET請求無法用實體傳參的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01

