JVM 全稱 Java Virtual Machine,也就是咱們耳熟能詳的 Java 虛擬機。它能識別 .class後綴的文件,而且可以解析它的指令,最終調用操做系統上的函數,完成咱們想要的操做。html
通常狀況下,使用 C++ 開發的程序,編譯成二進制文件後,就能夠直接執行了,操做系統可以識別它;可是 Java 程序不同,使用 javac 編譯成 .class 文件以後,還須要使用 Java 命令去主動執行它,操做系統並不認識這些 .class 文件。java
你可能會想,咱們爲何不能像 C++ 同樣,直接在操做系統上運行編譯後的二進制文件呢?而非要搞一個處於程序與操做系統中間層的虛擬機呢?數組
這就是 JVM 的過人之處了。你們都知道,Java 是一門抽象程度特別高的語言,提供了自動內存管理等一系列的特性。這些特性直接在操做系統上實現是不太可能的,因此就須要 JVM 進行一番轉換。瀏覽器
有了上面的介紹,咱們就能夠作以下的類比。
JVM:等同於操做系統;
Java 字節碼:等同於彙編語言。網絡
若是你仍是對上面的介紹有點模糊,能夠參考下圖:
從圖中能夠看到,有了 JVM 這個抽象層以後,Java 就能夠實現跨平臺了。JVM 只須要保證可以正確執行 .class 文件,就能夠運行在諸如 Linux、Windows、MacOS 等平臺上了。架構
而 Java 跨平臺的意義在於一次編譯,到處運行,可以作到這一點 JVM 功不可沒。好比咱們在 Maven 倉庫下載同一版本的 jar 包就能夠處處運行,不須要在每一個平臺上再編譯一次。併發
如今的一些 JVM 的擴展語言,好比 Clojure、JRuby、Groovy 等,編譯到最後都是 .class 文件,Java 語言的維護者,只須要控制好 JVM 這個解析器,就能夠將這些擴展語言無縫的運行在 JVM 之上了。oracle
咱們用一句話歸納 JVM 與操做系統之間的關係:JVM 上承開發語言,下接操做系統,它的中間接口就是字節碼。函數
而 Java 程序和咱們一般使用的 C++ 程序有什麼不一樣呢?這裏用兩張圖進行說明。
對比這兩張圖能夠看到 C++ 程序是編譯成操做系統可以識別的 .exe 文件,而 Java 程序是編譯成 JVM 可以識別的 .class 文件,而後由 JVM 負責調用系統函數執行程序。工具
JVM 是 Java 程序可以運行的核心。可是須要注意,JVM 本身什麼也幹不了,你須要給它提供生產原料(.class 文件)。俗語說的好,巧婦難爲無米之炊。它雖然功能強大,但仍須要爲它提供 .class 文件。
僅僅是 JVM,是沒法完成一次編譯,到處運行的。它須要一個基本的類庫,好比怎麼操做文件、怎麼鏈接網絡等。而 Java 體系很慷慨,會一次性將 JVM 運行所需的類庫都傳遞給它。JVM 標準加上實現的一大堆基礎類庫,就組成了 Java 的運行時環境,也就是咱們常說的 JRE(Java Runtime Environment)。
有了 JRE 以後,咱們的 Java 程序即可以在瀏覽器中運行了。你們能夠看一下本身安裝的 Java 目錄,若是是隻須要執行一些 Java 程序,只須要一個 JRE 就足夠了。
對於 JDK(Java Development Kit) 來講,就更龐大了一些。除了 JRE,JDK 還提供了一些很是好用的小工具,好比 javac、java、jar 等。它是 Java 開發的核心。
JVM、JRE、JDK 它們三者之間的關係,能夠用一個包含關係表示。
JDK>JRE>JVM
這裏的 Java 程序是文本格式的。好比下面這段 HelloWorld.java,它遵循的就是 Java 語言規範。其中,咱們調用了 System.out 等模塊,也就是 JRE 裏提供的類庫。
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
使用 JDK 的工具 javac 進行編譯後,會產生 HelloWorld 的字節碼。
咱們一直在說 Java 字節碼是溝通 JVM 與 Java 程序的橋樑,下面使用 javap 來稍微看一下字節碼到底長什麼樣子。
0 getstatic #2 <java/lang/System.out>
3 ldc #3 <Hello World>
5 invokevirtual #4 <java/io/PrintStream.println>
8 return
Java 虛擬機採用基於棧的架構,其指令由操做碼和操做數組成。這些字節碼指令,就叫做 opcode。其中,getstatic、ldc、invokevirtual、return 等,就是 opcode,能夠看到是比較容易理解的。
咱們繼續使用 hexdump 看一下字節碼的二進制內容。與以上字節碼對應的二進制,就是下面這幾個數字(能夠搜索一下)。
b2 00 02 12 03 b6 00 04 b1
咱們能夠看一下它們的對應關係。
0xb2 getstatic 獲取靜態字段的值
0x12 ldc 常量池中的常量值入棧
0xb6 invokevirtual 運行時方法綁定調用方法
0xb1 return void 函數返回
opcode 有一個字節的長度(0~255),意味着指令集的操做碼個數不能操做 256 條。而緊跟在 opcode 後面的是被操做數。好比 b2 00 02,就表明了 getstatic #2 <java/lang/System.out>。
JVM 就是靠解析這些 opcode 和操做數來完成程序的執行的。當咱們使用 Java 命令運行 .class 文件的時候,實際上就至關於啓動了一個 JVM 進程。
而後 JVM 會翻譯這些字節碼,它有兩種執行方式。常見的就是解釋執行,將 opcode + 操做數翻譯成機器代碼;另一種執行方式就是 JIT,也就是咱們常說的即時編譯,它會在必定條件下將字節碼編譯成機器碼以後再執行。
這些 .class 文件會被加載、存放到 metaspace 中,等待被調用,這裏會有一個類加載器的概念。
而 JVM 的程序運行,都是在棧上完成的,這和其餘普通程序的執行是相似的,一樣分爲堆和棧。好比咱們如今運行到了 main 方法,就會給它分配一個棧幀。當退出方法體時,會彈出相應的棧幀。你會發現,大多數字節碼指令,就是不斷的對棧幀進行操做。
而其餘大塊數據,是存放在堆上的。Java 在內存劃分上會更爲細緻。
最後你們看下面的圖,其中 JVM 部分,就是咱們的要點。
既然 JVM 只是一個虛擬機規範,那確定有很是多的實現。其中,最流行的要數 Oracle 的 HotSpot。
目前,最新的版本是 Java13(注意最新的LTS版本是11)。
爲了完成這個過程,你能夠打開瀏覽器,輸入下載網址(https://www.oracle.com/techne... javase/downloads/jdk13-downloads-5672538.html)並安裝軟件。固然你也能夠用稍低點的版本,可是有些知識點會有些許差別。
爲何 Java 研發系統須要 JVM?
JVM 解釋的是相似於彙編語言的字節碼,須要一個抽象的運行時環境。同時,這個虛擬環境也須要解決字節碼加載、自動垃圾回收、併發等一系列問題。JVM 實際上是一個規範,定義了 .class 文件的結構、加載機制、數據存儲、運行時棧等諸多內容,最經常使用的 JVM 實現就是 Hotspot。
對你 JVM 的運行原理了解多少?
JVM 的生命週期是和 Java 程序的運行同樣的,當程序運行結束,JVM 實例也跟着消失了。JVM 處於整個體系中的核心位置。
咱們寫的 Java 代碼究竟是如何運行起來的?
一個 Java 程序,首先通過 javac 編譯成 .class 文件,而後 JVM 將其加載到元數據
區,執行引擎將會經過混合模式
執行這些字節碼。執行時,會翻譯成操做系統相關的函數。JVM 做爲 .class 文件的黑盒存在,輸入字節碼,調用操做系統函數。
過程以下:Java 文件->編譯器>字節碼->JVM->機器碼。
咱們所說的 JVM,狹義上指的就 HotSpot。如非特殊說明,咱們都以 HotSpot 爲準。咱們瞭解到,Java 之因此成爲跨平臺,就是因爲 JVM 的存在。Java 的字節碼,是溝通 Java 語言與 JVM 的橋樑,同時也是溝通 JVM 與操做系統的橋樑。
JVM 是一個很是小的集合,咱們常說的 Java 運行時環境,就包含 JVM 和一部分基礎類庫。若是加上咱們經常使用的一些開發工具,就構成了整個 JDK。咱們講解 JVM 就聚焦在字節碼的執行上面。
Java 虛擬機採用基於棧的架構,有比較豐富的 opcode。這些字節碼能夠解釋執行,也能夠編譯成機器碼,運行在底層硬件上,能夠說 JVM 是一種混合執行的策略。