深刻淺出 JVM 系列(一)什麼是 JVM?它處於什麼位置?

閱讀本文大概須要 5.6 分鐘html

前言

在 Java 開發中,咱們常常會提到 JVM。咱們知道 JVM 是 Java 虛擬機,可是它的運行原理是什麼?它的內存結構是什麼?如何進行優化?如何去定位問題?面試中遇到 JVM 問題如何回答?java

接下來我會開啓 JVM 的章節,爲你們一一解答上面的問題。如今就開啓咱們的 JVM 學習之路吧!linux

什麼是 JVM?它處於什麼位置?

面試官:什麼是 JVM?nginx

小李:JVM(Java Virtual Machine)是 Java 虛擬機,用於運行 Java 編譯後的二進制字節碼,最後生成機器指令。(內心一想,簡簡單單)web

面試官:那爲何 Java 研發體系須要 JVM?你對 JVM 的運行原理了解多少?咱們寫的 Java 代碼究竟是如何運行起來的?面試

小李:嗯。。。就是。。。嗯。。。是那個。。。嗯。。。windows

面試官:面試就到這裏了,先回去等通知吧。數組

小李:好的!(哭着回答)網絡

img

這裏面試官對小李進行三連問:架構

  1. 爲何 Java 研發體系須要 JVM?
  2. 你對 JVM 的運行原理了解多少?
  3. 咱們寫的 Java 代碼究竟是如何運行起來的?

這套組合拳看似很厲害,其實就是軍體拳。

img

若是想完美的練這套軍體拳,不,是完美的回答這三個問題,就須要首先要了解 JVM 是什麼?它和 Java 是什麼關係?又和 JDK 有什麼淵源?那要弄清楚這些問題,就須要從三個維度去思考:

  • JVM 和操做系統的關係;
  • JVM 、JRE、JDK 的關係;
  • Java 虛擬機規範和 Java 語言規範的關係。

弄清楚這這幾者的關係,咱們再經過一個簡單代碼示例來看一個 Java 程序究竟是如何執行的。

JVM 和 操做系統的關係

咱們知道煉製一把牛逼的大寶劍,不只須要上等的技術,還須要一鼎經百鍊的劍爐。而工程師就至關於鑄劍的劍師,JVM 即是劍爐。

img

JVM 就是咱們耳熟能詳的 Java 虛擬機。它能識別 .class 後綴文件,而且可以解析它的指令,最終調用操做系統上的函數,完成咱們想要的操做。

Java 程序和 C++ 程序有什麼不一樣呢?這裏用兩張圖進行說明。

img img

對比兩張圖能夠看到 C++ 開發的程序能夠翻譯成操做系統能識別的 .exe 文件。而 Java 程序須要經過 javac 編譯成 .class 文件以後,而後由 JVM 負責調用系統函數執行程序,操做系統並不認識 .class 文件。

那讀者就勸小李了,轉 C++ 開發吧,這 Java 還搞了一個處於程序和操做系統的虛擬機,不像 C++ 編譯後直接在操做系統上運行,確定不是啥好玩意。

img

我就知道大家壞的很,知道 JVM 的過人之處,還不告訴小李。那我給小李講講 JVM 的過人之處:

Java 是一門抽象度特別高的語言,提供了自動內存管理等一系列的特性。這些特性在操做系統上基本上是無望了,因此就須要 JVM 進行一番轉換。

通過上面的介紹,咱們能夠作以下的類比:

  • JVM:等同於操做系統;
  • Java 字節碼:等同於彙編語言。

Java 字節碼仍是比較容易讀懂,從側面上也證實了 Java 語言的抽象程度高。咱們能夠認爲 JVM 是一個翻譯器,會持續不斷的翻譯執行 Java 字節碼,而後調用真正的操做系統函數,這些操做系統函數是與平臺息息相關的。

能夠把 JVM 想象一個有道詞典,.class 文件是英文,而輸出的結果是中文。有道詞典有 windows版本,也有 Linux 版本,內部具體的實現確定不一樣,但最終都會獲得相同的結果,這樣就好理解一些了)

img

當有個 JVM 這個抽象層,就能夠實現跨平臺了。JVM 只須要正確執行 .class 文件,就能夠運行在 Linux、Windos、MacOS 等平臺了。

Java 跨平臺的意義在於一次編譯,到處運行,這裏 JVM 功不可沒。好比在 Maven 倉庫下載的 jar 包就能夠處處運行,不須要在每一個平臺上再編譯一次。

img

咱們來歸納 JVM 與操做系統之間的關係

JVM 上承開發語言,下接操做系統,它的中間接口就是字節碼。

JVM、JRE、JDK 的關係

經過上面的學習,咱們瞭解到 JVM 是 Java 程序可以運行的核心。可是咱們要知道,JVM 本身什麼也幹不了,你須要給它提供原料(.class 文件)。俗話說:巧婦難爲無米之炊。JVM 功能雖然強大,但仍是須要爲它提供 .class 文件。

可是僅靠 JVM 是沒法完成一次編譯,處處運行的。它須要一個基本的類庫,好比怎麼操做文件、怎麼鏈接網絡、怎麼教你出拳(小李已瘋)等。而 Java 體系會一次性將 JVM 運行所需的類庫都傳遞給它。JVM 標準加上基本類庫就組成了 Java 的運行環境,就是 JRE (Java Runtime Enviroment)

JVM + 基本類庫 = JRE

那 JDK 又是什麼呢?

JDK 全稱 Java Development Kit,Kit 是裝備的意思。因此 JDK 不只包含 JRE,還有一些小工具,好比 javac、java、jar等。

JRE + javac/java/jar 等指令工具 = JDK

JVM、JRE、JDK 它們三者之間的關係,能夠用一個包含關係表示。

  • JDK > JRE > JVM
img

Java 虛擬機規範和 Java 語言規範的關係

從廣義上來說,JVM 是一種規範,它是最爲官方、準確的文檔;狹義上來說,因爲咱們使用 Hotspot 更多一些,因此咱們在談到這個概念時,會將他們等同起來。

若是再加咱們日常使用的 Java 語言,能夠獲得下面一張圖。

img

左邊是 Java 虛擬機規範,爲字節碼的解析提供一個環境。右邊是 Java 語法規範,好比 switch、for、泛型、lambda 等相關的程序,最終都會編譯成字節碼。而字節碼是連接左右兩部分的橋樑。

若是 .class 文件的規格是不變的,這兩部分是能夠獨立進行優化的。But 沒有若是,如今都已經到 Java 13 了,爲了支持更多的特性,確定會增長一些字節碼指令。

img

此刻優秀的小李提出了一個讓人深思的問題:

若是我不學習 JVM,會影響我寫 Java 代碼麼?

理論上,這二者沒有必然的聯繫。他們之間經過 .class 文件進行交互,即便你不瞭解 JVM,也可以寫大多數的 Java 代碼。就像你是寫 C++ 代碼同樣,並不須要特別深刻的瞭解操做系統的底層是如何實現的。

那我還學個錘子!瞬間關了該頁面。

img

客官別走,還有可是沒說呢。

可是,若是你想要寫一些比較精巧、效率比較高的代碼,就須要瞭解一些執行層面的知識了。瞭解 JVM,主要用在調優以及故障排查上面,你會對運行中的各類資源分配,有一個比較全面的掌控。(是否是心裏還有點小期待呢!)

img

Java 代碼究竟是如何運行起來的

最後,咱們簡單看一下 Java 程序的執行過程,瞭解下它究竟是如何運行起來的。

這裏的 Java 程序是文本格式的。好比下面這段 HelloXiaoli.java,它遵循的就是 Java 語言規範。其中,咱們調用的 System.out 等模塊,就是 JRE 提供的類庫。

img

經過 JDK 的工具 javac 進行編譯後,就會產生 HelloWorld 的字節碼。

1javac HelloXiaoli.java
img

Java 字節碼是溝通 JVM 和 Java 程序的橋樑,下面使用 javap 來看一下字節碼到底長什麼樣子。javap基本使用

javap -verbose HelloXiaoli.class
0 getstatic #2 <java/lang/System.out>
3 ldc #3 <Hello Xiaoli>
5 invokevirtual #4 <java/io/PrintStream.println>
8 return

Java 虛擬機採用基於棧的架構(爲何基於棧的架構詳見:JVM 體系結構與工做方式),其指令由操做碼和操做數組成。這些字節碼指令,就叫作 opcode。其中,getstatic、ldc、invokeevirtual、return 等,就是 opcode。

咱們繼續使用 hexdump 看一下字節碼的二進制內容hexdump 命令

b2 00 02 12 03 b6 00 04 b1

咱們能夠看一下它們的對應關係。JVM 字節碼對照表

0xb2   getstatic       獲取靜態字段的值
0x12   ldc             常量池中的常量值入棧
0xb6   invokevirtual   運行時方法綁定調用方法
0xb1   return          void 函數返回

opcode 是一個字節的長度(0~255),意味着指令集的操做碼個數不能超過 256 條。緊跟在 opcode 後面的是被操做數。好比 b2 00 02,就表明了 getstatic #2 。

JVM 就是靠解析這些 opcode 和 操做數來完成程序的執行的,當咱們使用 Java 命令運行 .class 文件的時候,實際上就至關於啓動了一個 JVM 進程。

JVM 會翻譯這些字節碼,它有兩種執行方式:

  • 解釋執行,將 opcode + 操做數翻譯成機器代碼;
  • JIT,即時編譯,它會在必定條件下將字節碼翻譯成機器碼以後再執行。

即時編譯器與解釋器的區別?

.class 文件會被加載、存放到 metaspace 中,等待被調用,這裏會有一個類加載器的概念。

JVM 的程序運行,都是在棧上完成的,這和其餘普通程序的執行是相似的,分爲堆和棧。好比咱們程序運行到了 main 方法,就會給它分配一個棧幀。當推出方法體時,會彈出相應的棧幀。其實,大多數字節碼指令,就是不斷的對棧幀進行操做。

而其它大塊數據,是存放在堆上的。Java 在內存劃分上會更爲細緻,關於這些概念,會在後面的章節中詳細介紹。

咱們看下面的圖,JVM 部分是咱們系列須要講解的部分。

img

小結

上面講了這麼多,讓咱們再回頭看看面試官提問的三個問題。

  • 爲何 Java 研發系統須要 JVM?

由於 Java 是一門抽象的語言,而且有自動內存管理機制。而操做系統沒法去進行自動垃圾回收等操做,因此就有了虛擬機。虛擬機能夠對字節碼加載、自動垃圾回收、併發等。而 JVM 只是一個規範,定義了 .class 文件的結構、加載機制、數據存儲、運行時棧等諸多內容,最經常使用的 JVM 實現就是 Hotspot。

  • 你對 JVM 的運行原理了解多少?

JVM 的生命週期是和 Java 程序的運行同樣,當程序運行結束,JVM 實例也就跟着消失了。具體的運行原理,會在後續文章中詳細介紹,請關注小李哦!

  • 咱們寫的 Java 代碼究竟是如何運行起來的?

Java 程序經過 javac 編譯成 .class 文件,而後虛擬機將其加載到元數據區,執行引擎將會經過混合模式執行這些字節碼。執行時,會翻譯成操做系統相關的函數。

過程以下:Java 文件->編譯器->字節碼->JVM->機器碼

總結

本篇文章從三個角度瞭解了 JVM 在 Java 研發體系中的位置,並以一個簡單的程序,看了下一個 Java 程序的執行過程。

咱們說的 JVM,狹義上指的就是 HotSpot。若是沒有特殊說明,咱們都以 HotSpot 爲準。

咱們知道 Java 之因此跨平臺,就是因爲 JVM 的存在。Java 的字節碼,是溝通 Java 語言與 JVM 的橋樑,同時也是溝通 JVM 與操做系統的橋樑。

JVM 是一個很是小的集合,咱們常說的 Java 運行時環境,也就是 JRE 包含 JVM 和一部分基礎類庫。若是加上咱們經常使用的一些開發工具,就構成了整個 JDK。

Java 虛擬機棧採用基於棧的架構,有比較豐富的 opcode。這些字節碼能夠解釋執行,也能夠編譯成機器碼,運行在底層硬件上,能夠說 JVM 是一種混合執行的策略。

留兩道思考題給你們:

  • 棧上都會有哪些數據?
  • 垃圾回收會發生在什麼地方?

思考題我會在後面的章節爲你們一一解答。

參考

http://pc-shop.xiaoe-tech.com/appcCrwMYBx6232/video_details?id=v_5e14662379d00_UAifIZpt

推薦閱讀

JVM 體系結構與工做方式

學習反射看這一篇就夠了

img
相關文章
相關標籤/搜索