JVM体系结构101:了解您的虚拟机
Java虛擬機(JVM)架構和Java字節碼101的初學者速成班
Java應用程序無處不在,它們在我們的手機,平板電腦和計算機上。 在許多編程語言中,這意味著要多次編譯代碼才能使其在不同的OS上運行。 對于作為開發人員的我們來說,關于Java的最酷的事情可能是Java的設計與平臺無關(俗話說“寫一次,在任何地方運行”),因此我們只需要編寫和編譯一次代碼。
這怎么可能? 讓我們深入研究Java虛擬機(JVM)進行查找。
JVM架構
這聽起來可能令人驚訝,但是JVM本身對Java編程語言一無所知。 相反,它知道如何執行自己的稱為Java字節碼的指令集,該指令集以二進制類文件形式組織。 Java代碼由javac命令編譯為Java字節碼,然后由JVM在運行時將其翻譯成機器指令。
線程數
Java被設計為并發的,這意味著可以通過在同一進程中運行多個線程來同時執行不同的計算。 當新的JVM進程啟動時,將在JVM中創建一個新線程(稱為main thread )。 從該主線程開始運行代碼,并可以生成其他線程。 實際的應用程序可以具有成千上萬個運行線程,這些線程可以滿足不同的目的。 有些服務于用戶請求,另一些則執行異步后端任務,等等。
堆棧和框架
每個Java線程都會與框架堆棧一起創建,該框架堆棧用于保存方法框架并控制方法的調用和返回。 方法框架用于存儲數據及其所屬方法的部分計算。 方法返回時,其框架將被丟棄。 然后,將其返回值傳遞回調用程序框架,現在可以使用它來完成自己的計算。
JVM流程結構
用于執行方法的JVM游樂場是方法框架。 框架包括兩個主要部分:
框架結構
幾乎每個字節碼命令都會操縱這兩個命令中的至少一個。 讓我們看看如何。
怎么運行的
讓我們來看一個簡單的示例,以了解不同元素如何一起運行以運行我們的程序。 假設我們有一個簡單的程序來計算2 + 3的值并打印結果:
class SimpleExample {public static void main(String[] args) {int result = add(2,3);System.out.println(result);}public static int add(int a, int b) {return a+b;} }要編譯此類,我們運行javac SimpleExample.java ,生成編譯文件SimpleExample.class 。 我們已經知道這是一個包含字節碼的二進制文件。 那么我們如何檢查類字節碼呢? 使用javap 。
javap是JDK附帶的命令行工具,可以反匯編類文件。 調用javap -c -p會打印出該類的反匯編字節碼(-c),包括私有(-p)成員和方法:
Compiled from "SimpleExample.java" class SimpleExample {SimpleExample();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."":()V4: returnpublic static void main(java.lang.String[]);Code:0: iconst_21: iconst_32: invokestatic #2 // Method add:(II)I5: istore_16: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;9: iload_110: invokevirtual #4 // Method java/io/PrintStream.println:(I)V13: returnpublic static int add(int, int);Code:0: iload_01: iload_12: iadd3: ireturn }現在,JVM在運行時會發生什么? java SimpleExample啟動一個新的JVM進程,并創建了主線程。 為main方法創建一個新框架,并將其推入線程堆棧。
public static void main(java.lang.String[]);Code:0: iconst_21: iconst_32: invokestatic #2 // Method add:(II)I5: istore_16: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;9: iload_110: invokevirtual #4 // Method java/io/PrintStream.println:(I)V13: return
main方法具有兩個變量: args和result 。 兩者都駐留在局部變量表中。 main的前兩個字節碼命令iconst_2和iconst_3將常量值2和3分別加載到操作數堆棧中。 下一個命令invokestatic調用靜態方法add。 由于此方法期望將兩個整數用作參數,因此invokestatic從操作數堆棧中彈出兩個元素,并將它們傳遞給JVM為add添加的新幀。 此時主操作數堆棧為空。
public static int add(int, int);Code:0: iload_01: iload_12: iadd3: ireturn
在添加框架中,這些參數存儲在局部變量數組中。 前兩個字節碼命令iload_0和iload_1將第0個和第1個局部變量加載到堆棧中。 接下來, iadd從操作數堆棧中彈出頂部的兩個元素,對其求和,然后將結果推回堆棧中。 最后, ireturn彈出頂部元素,并將其作為方法的返回值傳遞給調用框架,并丟棄該框架。
public static void main(java.lang.String[]);Code:0: iconst_21: iconst_32: invokestatic #2 // Method add:(II)I5: istore_16: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;9: iload_110: invokevirtual #4 // Method java/io/PrintStream.println:(I)V13: return main的堆棧現在保存add的返回值。 istore_1彈出它并將其設置為索引1處的變量的值,即result 。 getstatic將類型為java / io / PrintStream的靜態字段java / lang / System.out推入堆棧。 iload_1將索引1處的變量(現在等于5的結果值)壓入堆棧。 因此,此時堆棧擁有2個值:“ out”字段和值5。現在invokevirtual將要調用PrintStream.println方法。 它從堆棧中彈出兩個元素:第一個是對將要調用println方法的對象的引用。 第二個元素是要傳遞給println方法的整數參數,該參數需要一個參數。 這是main方法打印add結果的地方。 最后, 返回命令完成該方法。 主機被丟棄,JVM進程結束。
就是這個。 總而言之,不太復雜。
“一次編寫,隨處運行”
那么,什么使Java與平臺無關? 全部都在字節碼中。
如我們所見,任何Java程序都可以編譯為標準Java字節碼。 然后,JVM在運行時將其轉換為特定的機器指令。 我們不再需要確保我們的代碼與機器兼容。 相反,我們的應用程序可以在裝有JVM的任何設備上運行,并且JVM將為我們完成此操作。 JVM維護人員的工作是提供不同版本的JVM以支持不同的機器和操作系統。
這種體系結構使任何Java程序都可以在安裝了JVM的任何設備上運行。 這樣魔術就發生了。
最后的想法
Java開發人員可以在不了解JVM如何工作的情況下編寫出色的應用程序。 但是,深入研究JVM體系結構,學習其結構并了解其如何解釋代碼將有助于您成為更好的開發人員。 它還將幫助您不時解決非常復雜的問題
PS。 如果您想更深入地研究JVM以及所有這些與Java異常的關系,那就別無所求! ( 這里可以。 )
翻譯自: https://www.javacodegeeks.com/2018/05/jvm-architecture-101-get-to-know-your-virtual-machine.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的JVM体系结构101:了解您的虚拟机的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: javafx响应式布局_JavaFX的响
- 下一篇: 电脑椅报价(电脑桌椅子价格及图片)