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

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

1. 第一步:類加載框架
.class 文件里包含了類的所有信息(類名、父類、接口、字段、方法、訪問修飾符等),類加載系統(tǒng)的作用,就是把這些 .class 文件加載到JVM的“內(nèi)存區(qū)域”(運(yùn)行時(shí)數(shù)據(jù)區(qū))。
2. 第二步:運(yùn)行時(shí)數(shù)據(jù)區(qū)
加載后的類、代碼執(zhí)行時(shí)產(chǎn)生的數(shù)據(jù)(比如對(duì)象、變量、方法調(diào)用信息),都會(huì)存放在這里。它相當(dāng)于JVM的“工作內(nèi)存”,主要分為5個(gè)部分:
| 內(nèi)存區(qū)域 | 核心作用 |
|---|---|
| 方法區(qū)(Method Area) | 存放已加載類的元數(shù)據(jù)(類名、父類、接口、字段、方法等),比如你定義的 User 類信息就存在這 |
| 堆(Heap) | 存放代碼中 new 出來的對(duì)象(比如 User user = new User()),是JVM中最大的內(nèi)存區(qū)域 |
| 虛擬機(jī)棧(VM Stack) | 存放方法調(diào)用的信息(比如調(diào)用 main() 方法、add() 方法時(shí),會(huì)創(chuàng)建“棧幀”存在這里),方法執(zhí)行完棧幀就銷毀 |
| 程序計(jì)數(shù)器(Program Counter Register) | 記錄當(dāng)前線程執(zhí)行到哪一行字節(jié)碼指令(多線程時(shí),切換線程后能恢復(fù)執(zhí)行位置,避免亂序) |
| 本地方法棧(Native Method Stack) | 存放Java調(diào)用的“本地方法”(用C/C++寫的方法,比如Java底層的IO操作、線程管理)的調(diào)用信息 |
3. 第三步:執(zhí)行引擎
.class 文件里的字節(jié)碼,只是JVM能識(shí)別的“指令集規(guī)范”,不是操作系統(tǒng)能直接執(zhí)行的機(jī)器指令——這時(shí)候就需要執(zhí)行引擎來“翻譯”。
執(zhí)行引擎的核心工作:
- 讀取運(yùn)行時(shí)數(shù)據(jù)區(qū)里的字節(jié)碼指令;
- 把字節(jié)碼指令“解釋”(或編譯)成當(dāng)前操作系統(tǒng)能識(shí)別的機(jī)器指令;
- 把機(jī)器指令交給CPU執(zhí)行。
這里要提一句:Java不是純“解釋執(zhí)行”,還會(huì)用到JIT(Just-In-Time 即時(shí)編譯器)——熱點(diǎn)代碼(頻繁執(zhí)行的代碼)會(huì)被JIT編譯成機(jī)器指令緩存起來,下次執(zhí)行直接用,不用重復(fù)解釋,這樣能提升性能。Java 編譯器把 .java 文件編譯為字節(jié)碼文件后,JVM 執(zhí)行引擎將字節(jié)碼指令解釋為對(duì)應(yīng)操作系統(tǒng)能運(yùn)行的指令并執(zhí)行,這也就是為什么說 Java 是編譯和解釋共存的語言。
4. 第四步:本地方法接口(JNI)+ 本地方法庫
用C/C++寫的“本地方法”,存放在本地方法庫中。就是Java代碼執(zhí)行時(shí),會(huì)遇到一些“Java做不了”的操作(比如操控硬件、底層IO、線程調(diào)度),這些操作都
前文也提到了,本地辦法執(zhí)行時(shí),相關(guān)信息會(huì)放到運(yùn)行時(shí)數(shù)據(jù)區(qū)的本地方法棧里。
到此這篇關(guān)于java多線程的文章就介紹到這了,更多相關(guān)java多線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中使用Filter控制用戶登錄權(quán)限具體實(shí)例
java中使用Filter控制用戶登錄權(quán)限具體實(shí)例,需要的朋友可以參考一下2013-06-06
SpringBoot實(shí)現(xiàn)對(duì)超大文件進(jìn)行異步壓縮下載的使用示例
在Web應(yīng)用中,文件下載功能是一個(gè)常見的需求,本文介紹了SpringBoot實(shí)現(xiàn)對(duì)超大文件進(jìn)行異步壓縮下載的使用示例,具有一定的參考價(jià)值,感興趣的可以了解一下,2023-09-09
深入了解Java SpringBoot自動(dòng)裝配原理
在使用springboot時(shí),很多配置我們都沒有做,都是springboot在幫我們完成,這很大一部分歸功于springboot自動(dòng)裝配。本文將詳細(xì)為大家講解SpringBoot的自動(dòng)裝配原理,需要的可以參考一下2022-03-03
SpringCLoud搭建Zuul網(wǎng)關(guān)集群過程解析
這篇文章主要介紹了SpringCLoud搭建Zuul網(wǎng)關(guān)集群過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
Java8的Stream()與ParallelStream()的區(qū)別說明
這篇文章主要介紹了Java8的Stream()與ParallelStream()的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Springboot使用thymeleaf動(dòng)態(tài)模板實(shí)現(xiàn)刷新
這篇文章主要介紹了Springboot使用thymeleaf動(dòng)態(tài)模板實(shí)現(xiàn)刷新,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08

