国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JVM體系結構與工作方式概覽

suosuopuo / 3422人閱讀

摘要:在本文,筆者將與大家概覽的體系結構與工作方式。將第條和第條指令分別是將兩個局部變量入棧,然后相加。最后一條指令是,這條指令執行完后當前的這個方法對應的這些部件會被回收,局部變量區的所有值將全部釋放,寄存器會被銷魂,在棧中與這個方

Java之所以號稱“一次編譯,到處運行”,主要原因是JVM屏蔽了各個計算機平臺相關的軟件(大多指系統)或者硬件之間的差異,使得與平臺相關的耦合統一由JVM提供者來實現。在本文,筆者將與大家概覽JVM的體系結構與工作方式。

JVM體系結構詳解

JVM和實體機器的體系結構有點相似,主要由以下幾個部分組成:

自己的指令集(篇幅過大,這里不會描述)

類加載器(在JVM啟動時或者在類運行時將需要的class加載到JVM中)

執行引擎(執行引擎的任務是負責執行class文件中包含的字節碼指令,相當于實際機器上的CPU)

內存區(將內存劃分成若干區以模擬實際機器上的存儲、記錄和調度功能模塊,如實際機器上的各種功能的寄存器或者PC指針的記錄器等)

本地方法調用(調用C或C++實現的本地方法的代碼返回結果)

下面簡單介紹一下

執行引擎

執行引擎是JVM的核心部分,執行引擎的作用就是解析JVM字節碼指令,得到執行結果。JVM虛擬機規范詳細地定義了執行引擎遇到每條字節碼指令時應該處理什么,并且應該得到什么結果。但是并沒有規定執行引擎應該如何或采用什么方式處理而得到這個結果。因為執行引擎具體采取什么方式由JVM的實現廠家自己去實現,是直接解釋執行還是采用JIT轉換成本地代碼去執行,還是采用寄存器這個芯片模式去執行都可以。所以執行引擎的具體實現有很大的發揮空間,如SUN的hotspot的基于棧的執行引擎,而Google的Dalvik的基于寄存器的執行引擎。

執行引擎也就是執行一條條代碼的一個流程,而代碼都是包含在方法體內的,所以執行引擎本質上就是執行一個個方法所串起來的流程,對應到操作系統中一個執行流程是一個Java進程還是一個Java線程呢?很顯然是后者,因為一個Java進程可以有多個同時執行的執行流程。這樣說來每個Java線程就是一個執行引擎的實例,那么在一個JVM實例中就會同時有多個執行在引擎在工作,這些執行引擎有的在執行用戶的程序,有的在執行JVM內部的程序(如Java垃圾收集器)。

Java內存管理

執行引擎在執行一段程序時需要存儲一些東西,如:操作碼需要的操作數,操作碼執行結果需要保存。Class類的字節碼還有類的對象等信息都需要在執行引擎執行之前就準備好。一個JVM實例會有一個方法區、Java堆、Java棧、PC寄存器和本地方法區。其中方法區和Java堆是所有線程共享的,也就是可以被所有執行引擎實例訪問。每個新的執行引擎實例被創建時會為這個執行引擎創建一個Java棧和一個PC寄存器,如果當前正在執行一個Java方法,那么在當前的這個Java棧中保存的是該線程中方法調用的狀態,包括方法的參數、方法的局部變量、方法的返回值以及運算的中間結果等。而PC寄存器會指向即將執行的下一個指令。

如果是本地方法調用,則存儲在本地方法調用棧中或者特定實現中的某個內存區域中。

考慮到篇幅大小,故另寫一篇文章:淺析JVM之內存管理

JVM工作機制

之前簡單分析了JVM的基本結構,下面再簡單分析一下JVM是如何執行字節命令的,也就是前面介紹的執行引擎是如何工作的。

我們知道,計算機只接受機器指令,其他高級語言必須先經過編譯器編譯成機器指令才能被計算機正確執行。然而機器語言一般和硬件平臺密切相關(指令集、CPU架構的因素等),但高級語言會屏蔽所有底層硬件平臺甚至軟件平臺。之所以可以屏蔽是因為中間有個編譯環節,與硬件耦合的麻煩就交給了編譯器。所以,想說的是:編譯器和操作系統的關系非常密切。比如C語言在win下編譯器為Microsoft C,而Linux下通常是gcc。

通常一個程序從編寫到執行會經歷以下階段:

源代碼(source code)

預處理器(preprocessor)

編譯器(compiler)

匯編程序(assembler)

目標代碼(object code)

鏈接器(linker)

可執行程序(executables)

除了1、7兩步,其他都是由現代意義上的編譯器統一完成的。最常見的栗子是在Linux平臺下我們通常安裝一個軟件需要經過configure、make、make install、make clean這4個步驟來完成。

configure為這個程序在當前的操作系統環境下選擇適合的編譯器來編譯這個程序代碼,也就是為這個程序代碼選擇合適的編譯器和一些環境參數;

make 可以猜到:對程序代碼進行編譯操作,將源碼編譯可執行的目標文件

make install 將已經編譯好的可執行文件安裝到操作系統指定或者默認的安裝目錄下

make clean 用刪除編譯時臨時產生的目標文件

值得注意的是,我們通常所說的是編譯器都是將某種高級語言直接編譯成可執行的目標機器語言(實際上在某種操作系統中是ixuyao動態連接的二進制文件:在Windows下是dynamic link library,Dll;在linux下是Shared library,SO庫)。但是實際上還有一些編譯是將一種高級語言編譯成另一種高級語言,或者將低級語言編譯成高級語言(反編譯),或者將高級語言編譯成虛擬機目標語言,如Java編譯器等。

再回到如何讓機器(不管是實體機還是虛擬機)執行代碼的主題,不管是如何指令集都只有集中最基本的元素:加、減、乘、除、求余、求模等。這些運算又可以進一步分解成二進制位運算:與、或、異或等。這些運算又通過指令來完成,而指令的核心目的就是確定需要運算的種類(操作碼)和運算需要的數據(操作數),以及從哪里(寄存器或棧)獲取操作數、將運算結果存放到什么地方(寄存器或是棧)等。這種不同的操作方式又將指令劃分成:一地址指令、二地址指令、三地址指令和零地址指令等n地址指令。相應的指令集會有對應的架構實現,如基于寄存器的架構實現或基于棧的架構實現,這里的基于寄存器或棧都是指在一個指令中的操作數是如何存取的。

JVM為何選擇基于棧的架構

學過數據結構的小伙伴都知道,對棧進行操作是要先將所有的操作數壓入棧,然后根據指令中操作碼選擇一定的元素彈出計算后再壓入棧。相對于寄存器操作(將兩個操作數存入寄存器后進行加法運算后再將加過存入其中一個寄存器即可)是比較麻煩的。那么,JVM為什么還要基于棧來設計呢

JVM要設計成與平臺無關

有些平臺上的寄存器很少或者根本沒有,而且以處理器架構的角度來說,設計一套通用的寄存器指令是很困難的。比如在android上,google的Dalvik VM就是基于ARM平臺設計的寄存器架構,這樣性能上的確更優了,但是犧牲了跨平臺的移植性。

為了指令的緊湊型

執行引擎的架構設計

每當創建一個新的線程時,JVM會為這個線程創建一個Java棧,同時會為這個線程分配一個PC寄存器,并且這個PC寄存器會指向這個線程的第一行可執行代碼。每當調用一個新方法時會在這個棧上創建一個新的棧幀數據結構,這個幀棧會保留這個方法的一些元信息——如這個方法中定義的局部變量、一些用來支持常量池的解析、正常方法返回及異常處理機制等。

JVM調用某些指令時可能需要使用到常量池中的一些常量,或者是獲取常量代表的數據或者這個數據指向的實例化對象,而這些信息都存儲在所有線程共享的方法區和Java堆中。

 執行引擎的執行過程

下面以一個簡單的程序來說明執行引擎的執行過程。

public class Math{
  public static void main(String[]args){
    int a=1;
    int b=2;
    int c = (a+b)*10;
  }
}

其中對應的字節碼指令如下:

偏移量 指令 說明
0 iconst_1 常數1入棧
1 istore_1 將棧頂元素移入本地變量1存儲
2 iconst_1 常數2入棧
3 istore_2 將棧頂元素移入本地變量1存儲
4 iload_1 本地變量1入棧
5 iload_2 本地變量2入棧
6 iadd 彈出棧頂兩個元素相加
7 bipush 10 將10入棧
9 imul 棧頂兩個元素相乘
10 istore_3 棧頂元素移入本地變量3存儲
11 return 返回

對應到執行引擎的各執行部件如圖

在開始執行方法之前,PC寄存器存儲的指針是第1條指令的地址,局部變量區和操作棧都沒有數據。從第1條和第4條指令分別將a、b兩個本地變量賦值,對應到局部變量區就是1和2分別存儲常數1和2。

前4條指令執行完后,PC寄存器當前指向的是下一條指令地址,也就是第5條指令,這時局部變量區已經保存了兩個局部變量(也就是變量a和變量b的值),而操作棧里仍然沒有值,因為兩次常數入棧后又分別出棧了。

將第5條和第6條指令分別是將兩個局部變量入棧,然后相加。如圖

1先入棧2后入棧,棧頂元素是2,第7條指令是棧頂的兩個元素彈出后相加,將結果再入棧,這時整個部件狀態如圖

可以看出,變量a和變量b想加的結果3存在當前棧的棧頂中,接下來是第8條指令將10入棧,如圖

當前PC寄存器執行的地址是9,下一個操作是將當前棧的兩個操作數彈出進行相乘并把結果壓入棧,如圖

第10條指令是將當前的棧頂元素存入局部變量3中,這是狀態如圖

第10條指令執行完后棧中元素出棧,出棧的元素存儲在局部變量區3中,對應的是變量c的值。最后一條指令是return ,這條指令執行完后當前的這個方法對應的這些部件會被JVM回收,局部變量區的所有值將全部釋放,PC寄存器會被銷魂,在Java棧中與這個方法對應的棧幀將消失。

 JVM方法調用棧

JVM的方法調用分別為兩種:

Java方法調用

本地方法調用

由于本地方法調用各個虛擬機的實現不太相同,所以這里主要介紹Java的方法調用情況。

public class Math{
  public static void main(String[]args){
    int a =1;
    int b=2;
    int c=math(a,b)/10;
  }
  public static int math(int a, int b){
    return (a+b)*10;
  }
}

那么其中兩個方法對應的字節碼分別如下:

public static void main(java.lang.String[]);
  Code:
    0:  iconst_1
    1:  istore_1
    2:  iconst_2
    3:  istore_2
    4:  iload_1
    5:  iload_2
    6:  invokestatic  #2; //Method math:(II)
    9:  bipush 10
    11: idiv
    12: istore_3
    13: return

public static int math(int ,int );
  Code:
    0: iload_0
    1: iload_1
    2: iadd
    3: bipush 10
    5: imul
    6: ireturn                        

當JVM執行main方法時,首先將常數1和2分別存儲到局部變量區1和2中,然后調用靜態math方法。從math的字節碼指令可以看出,math方法的兩個參數也存儲在其對應的方法棧幀中的局部變量區0和1中,先將這兩個局部變量分別入棧,然后進行相加操作再和常數10相乘。

那么來看一下實際的操作,如圖

上圖是JVM執行到第5條指令時,執行引擎各部件的狀態圖,PC寄存器指向的是下一條執行math方法的地址。當執行invokestatic指令時JVM會為math方法創建一個新的棧幀,并且將兩個參數存在math方法的棧幀的前兩個局部變量區中,這時PC寄存器會清零,并且會指向math方法對應棧幀的第一條指令地址,這時的狀態如下圖

執行invokestatic指令時,創建了一個新的棧幀,這是棧幀的局部變量中已經有了兩個變量了,這兩個變量是從main方法的棧幀中的操作棧中傳過來的。當執行math方法時,math方法對應的棧幀成為當前的活動棧幀,PC寄存器保存的是當前這個戰爭中的下一條指令地址,所以是0。

math方法先將a、b兩個變量相加,再乘以10,最后返回這個結果執行到第5條指令的狀態,如下圖

math的操作棧中的棧頂元素相乘的結果是30,最后一條指令是ireturn,這條指令是將當前棧幀中的棧頂元素返回到調用這個方法的棧中,而這個棧幀也將撤銷,PC寄存器的值回復調用棧的下一條指令地址,如下圖

main方法將math方法返回的結果再除以10存放在變量區3中,這時的狀態如圖所示

當執行return指令時main方法對應的棧幀也將撤銷,如果當前線程對應的Java棧中沒有棧幀,這個Java棧也將被JVM撤銷,整個JVM退出。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/65148.html

相關文章

  • 利用MAT分析JVM內存問題,從入門到精通(二)

    摘要:用于列舉最近分析過的文件常用功能欄,從左到右依次是概覽類直方圖支配樹查詢線程視圖報告相關詳細功能。針對那些占用堆內存超過整個堆內存大小的組件做一系列的分析,例如保留集合潛在的內存浪費問題等其他問題。 上一篇文章MAT入門到精通(一)介紹了MAT的使用場景和基本概念,這篇文章開始介紹MAT的基本功能,后面還有兩篇,一篇是MAT的高級功能,另一篇是MAT實戰案例分析。 三、歡迎頁 使用MA...

    amuqiao 評論0 收藏0
  • jvm基礎篇一之內存區域

    摘要:堆區堆是虛擬機所管理的內存中最大的一塊,它是被所有線程共享的一塊內存區域,該區域在虛擬機啟動的時候創建。 運行時數據區域 ? ?想要了解jvm,那對其內存分配管理的學習是必不可少的;java虛擬機在執行java程序的時候會把它所管理的內存劃分成若干數據區域。這些區域有著不同的功能、用途、創建/銷毀時間。java虛擬機所分配管理的內存區域如圖1所示 程序計數器 ? ?程序計數器是一塊比較...

    Zachary 評論0 收藏0
  • JVM詳解3.JDK監控和故障處理工具

    摘要:點擊進入我的博客命令行工具這些工具大多數是類庫的一層薄的包裝,它們的主要功能代碼是在類庫中實現的。可視化工具是到目前為止隨發布的功能最強大的運行監視和故障處理程序,并且可以預見在未來一段時間內都是官方主力發展的虛擬機故障處理工具。 點擊進入我的博客 3.1 JDK命令行工具 showImg(https://segmentfault.com/img/remote/14600000174...

    Keven 評論0 收藏0
  • jvm體系結構和gc調優(一)

    摘要:做好的優化能大大提升系統的性能體系結構概覽大致流程如圖編譯好的文件通過類加載器從物理結構轉換成運行時數據區結構。后面再寫一篇關于調優的 什么是jvm jvm是java虛擬機的縮寫。所有的java程序都是在jvm上運行的。做好jvm的優化能大大提升系統的性能 jvm體系結構概覽 showImg(https://segmentfault.com/img/bVba5lB?w=1049&h=6...

    wupengyu 評論0 收藏0
  • Angular(01)-- 架構概覽

    摘要:正文架構概覽正文架構概覽接觸大概一個月吧,期間寫了個項目,趁現在稍微有點時間,來回顧梳理一下。里的模塊,并不等同于項目中的模塊概念。當然,這只是我目前階段的理解。聲明 本系列文章內容梳理自以下來源: Angular 官方中文版教程 官方的教程,其實已經很詳細且易懂,這里再次梳理的目的在于復習和鞏固相關知識點,剛開始接觸學習 Angular 的還是建議以官網為主。 因為這系列文章,更多的會...

    bitkylin 評論0 收藏0

發表評論

0條評論

suosuopuo

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<