我們一起聊聊JVM優(yōu)化:JVM概述
一、什么是JVM
JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用于計算設(shè)備的規(guī)范,它是一個虛構(gòu)出來的計算 機,是通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的。
二、JVM與操作系統(tǒng)
Java 是一門抽象程度特別高的語言,提供了自動內(nèi)存管理等一系列的特性。這些特性直接在操作系統(tǒng)上實現(xiàn)是不太 可能的,所以就需要 JVM 進行一番轉(zhuǎn)換。
從圖中可以看到,有了 JVM 這個抽象層之后,Java 就可以實現(xiàn)跨平臺了。JVM 只需要保證能夠正確執(zhí)行 .class 文 件,就可以運行在諸如 Linux、Windows、MacOS 等平臺上了。 而 Java 跨平臺的意義在于一次編譯,處處運行,能夠做到這一點 JVM 功不可沒。比如我們在 Maven 倉庫下載同一 版本的 jar 包就可以到處運行,不需要在每個平臺上再編譯一次。 現(xiàn)在的一些 JVM 的擴展語言,比如 Clojure、JRuby、Groovy 等,編譯到最后都是 .class 文件,Java 語言的維護 者,只需要控制好 JVM 這個解析器,就可以將這些擴展語言無縫的運行在 JVM 之上了。
我們用一句話概括 JVM 與操作系統(tǒng)之間的關(guān)系:JVM 上承開發(fā)語言,下接操作系統(tǒng),它的中間接口就是字節(jié)碼。
三、JVM、JRE、JDK 的關(guān)系
JVM 是 Java 程序能夠運行的核心。但是需要注意,JVM 自己什么也干不了,你需要給它提供生產(chǎn)原料(.class 文 件) 。
僅僅是 JVM,是無法完成一次編譯,處處運行的。它需要一個基本的類庫,比如怎么操作文件、怎么連接網(wǎng)絡(luò)等。 而 Java 體系很慷慨,會一次性將 JVM 運行所需的類庫都傳遞給它。JVM 標準加上實現(xiàn)的一大堆基礎(chǔ)類庫,就組成 了 Java 的運行時環(huán)境,也就是我們常說的 JRE(Java Runtime Environment) 對于 JDK 來說,就更龐大了一些。除了 JRE,JDK 還提供了一些非常好用的小工具,比如 javac、java、jar 等。它 是 Java 開發(fā)的核心,讓外行也可以煉劍!
我們也可以看下 JDK 的全拼,Java Development Kit。我非常怕 kit(裝備)這個單詞,它就像一個無底洞,預(yù)示著 你永無休止的對它進行研究。JVM、JRE、JDK 它們?nèi)咧g的關(guān)系,可以用一個包含關(guān)系表示。
四、Java虛擬機規(guī)范和 Java 語言規(guī)范的關(guān)系
左半部分是 Java 虛擬機規(guī)范,其實就是為輸入和執(zhí)行字節(jié)碼提供一個運行環(huán)境。右半部分是我們常說的 Java 語法 規(guī)范,比如 switch、for、泛型、lambda 等相關(guān)的程序,最終都會編譯成字節(jié)碼。而連接左右兩部分的橋梁依然是 Java 的字節(jié)碼。
如果 .class 文件的規(guī)格是不變的,這兩部分是可以獨立進行優(yōu)化的。但 Java 也會偶爾擴充一下 .class 文件的格式, 增加一些字節(jié)碼指令,以便支持更多的特性。
我們可以把 Java 虛擬機可以看作是一臺抽象的計算機,它有自己的指令集以及各種運行時內(nèi)存區(qū)域,學過《計算 機組成結(jié)構(gòu)》的同學會在課程的后面看到非常多的相似性。
最后,我們簡單看一下一個 Java 程序的執(zhí)行過程,它到底是如何運行起來的
這里的 Java 程序是文本格式的。比如下面這段 HelloWorld.java,它遵循的就是 Java 語言規(guī)范。其中,我們調(diào)用了 System.out 等模塊,也就是 JRE 里提供的類庫。
使用 JDK 的工具 javac 進行編譯后,會產(chǎn)生 HelloWorld 的字節(jié)碼。 我們一直在說 Java 字節(jié)碼是溝通 JVM 與 Java 程序的橋梁,下面使用 javap 來稍微看一下字節(jié)碼到底長什么樣子。
Java 虛擬機采用基于棧的架構(gòu),其指令由操作碼和操作數(shù)組成。這些 字節(jié)碼指令 ,就叫作 opcode。其中, getstatic、ldc、invokevirtual、return 等,就是 opcode,可以看到是比較容易理解的。 JVM 就是靠解析這些 opcode 和操作數(shù)來完成程序的執(zhí)行的。當我們使用 Java 命令運行 .class 文件的時候,實際上 就相當于啟動了一個 JVM 進程。
然后 JVM 會翻譯這些字節(jié)碼,它有兩種執(zhí)行方式。常見的就是解釋執(zhí)行,將 opcode + 操作數(shù)翻譯成機器代碼;另 外一種執(zhí)行方式就是 JIT,也就是我們常說的即時編譯,它會在一定條件下將字節(jié)碼編譯成機器碼之后再執(zhí)行。