国产精品三级国产专区,国产欧美二区亚洲综合,欧美精品专区一中文字在线观看,国产免费A∨片在线播放

  • 
    
  • <mark id="t4lup"><thead id="t4lup"><input id="t4lup"></input></thead></mark>

    <s id="t4lup"></s>

      湖北企業(yè)新聞網(wǎng),歡迎您!

      幫助中心 廣告聯(lián)系

      網(wǎng)站關(guān)鍵詞: 湖北企業(yè)新聞網(wǎng)

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解!

      來源:時(shí)間:2020-11-19 09:22:27 閱讀:-

      什么是JVM

      這個大家都應(yīng)該很熟悉了吧,JVM不就是虛擬機(jī)嗎?

      Java虛擬機(jī)本質(zhì)上就是一個程序,當(dāng)它在命令行上啟動的時(shí)候,就開始執(zhí)行保存在某字節(jié)碼文件中的指令。

      JVM可以說離我們既熟悉又陌生,很多朋友可能在工作中接觸不到這塊技術(shù),但是在面試往往被問到(概率還蠻大),被問到了自認(rèn)倒霉,死記硬背是沒用的,到頭來還是的忘,今天給大家說道說道JVM知識點(diǎn),我要沒讓你明白算我輸,你可以留言噴我,如果要是可以,你們也給我點(diǎn)個贊成不?


      初識JVM

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      相信這張圖大家都不陌生,這是整個Java體系,其中包括JDK.JRE.JVM三者的關(guān)系。

      圖中可以看得出來JRE包含了JVM,JDK包含了JRE。

      從包含的角度就是: JDK是爺爺 JRE是父親 JVM是兒子(如果覺得列子不太恰當(dāng))來看圖

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      我們來看代碼:

      public class App {

      private String name;

      private Object object = new Object();

      /***

      * 運(yùn)算

      */

      public int add() {

      int a = 1;

      int b = 2;

      int c = (a + b) * 100;

      return c;

      }

      /**

      * 程序入口

      */

      public static void main(String[] args) throws InterruptedException {

      App app = new App();

      int result = app.add();

      System.out.println(result);

      }

      }

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      我們運(yùn)行上述代碼輸出結(jié)果是300,雖然這個代碼非常簡單,這個時(shí)候已經(jīng)涉及到JVM相關(guān)的知識了,在我們學(xué)Java基礎(chǔ)的時(shí)候老師就告訴我們,Java是跨平臺的,一次編寫到處運(yùn)行。

      那Java是怎么做到跨平臺的?繼續(xù)看下圖:

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      通過此圖大家就不難發(fā)現(xiàn),我們編譯的App.class文件可以在Windows操作系統(tǒng)運(yùn)行也可以在Linux系統(tǒng)運(yùn)行,但是兩個系統(tǒng)底層的操作指令是不一樣的,為了屏蔽底層指令的細(xì)節(jié),起到一個跨平臺的作用,JVM功不可沒,我們常說Java是跨平臺還不如說是Jvm跨平臺(JRE運(yùn)行時(shí)跨平臺)。那Jvm虛擬機(jī)是怎么跨平臺的?

      JVM底層原理

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      JVM底層由三個系統(tǒng)構(gòu)成分別是:類加載、運(yùn)行時(shí)數(shù)據(jù)區(qū)、執(zhí)行引擎。

      我們今天重點(diǎn)講解JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)(棧),其他兩塊可以關(guān)注我之前和后續(xù)文章。

      我們App.class文件通過類加載子系統(tǒng)從硬盤中讀取文件加載到內(nèi)存中(運(yùn)行時(shí)數(shù)據(jù)區(qū))。

      加載完成之后怎么處理了?(打個比喻 人吃飯 》吃到肚子里》各各器官負(fù)責(zé)自己工作吸收)

      Stack棧

      先講一下其中的一塊內(nèi)存區(qū)域虛擬機(jī)棧,大家都知道棧是數(shù)據(jù)結(jié)構(gòu),也是線程獨(dú)有的區(qū)域,也就是每一個線程都會有自己獨(dú)立的棧區(qū)域。我們運(yùn)行App.java輸出300就靠線程執(zhí)行得來的結(jié)果。是哪個線程執(zhí)行的?獲取線程快照:“main線程”

      ?!窋?shù)據(jù)結(jié)構(gòu)》存儲內(nèi)容》先進(jìn)后出FILO

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      大家都知道每個方法都有自己的局部變量,比如上圖中main方法中的result,add方法中的a b c,那么java虛擬機(jī)為了區(qū)分不同方法中局部變量作用域范圍的內(nèi)存區(qū)域,每個方法在運(yùn)行的時(shí)候都會分配一塊獨(dú)立的棧幀內(nèi)存區(qū)域,我們試著按上圖中的程序來簡單畫一下代碼執(zhí)行的內(nèi)存活動。

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      執(zhí)行main方法中的第一行代碼是,棧中會分配main()方法的棧幀,并存儲math局部變量,,接著執(zhí)行add()方法,那么棧又會分配add()的棧幀區(qū)域。

      這里的棧存儲數(shù)據(jù)的方式和數(shù)據(jù)結(jié)構(gòu)中學(xué)習(xí)的棧是一樣的,先進(jìn)后出。當(dāng)add()方法執(zhí)行完之后,就會出棧被釋放,也就符合先進(jìn)后出的特點(diǎn),后調(diào)用的方法先出棧。

      棧幀

      棧幀內(nèi)部“數(shù)據(jù)結(jié)構(gòu)”主要由這幾個部分組成:局部變量表、操作數(shù)棧、方法出口等信息。

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      說了半天,棧幀到底干嘛用的呀?別急講這個就會涉及到更底層的原理–字節(jié)碼。我們先看下我們上面代碼的字節(jié)碼文件。

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      APP.class文件看著像亂碼,其實(shí)每個都是有對應(yīng)的含義的,oracle官方是有專門的jvm字節(jié)碼指令手冊來查詢每組指令對應(yīng)的含義的。那我們研究的,當(dāng)然不是這個。

      jdk有自帶一個javap的命令,可以將上述class文件生成一種更可讀的字節(jié)碼文件。

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      Compiled from "App.java"

      public com.App {

      public com.App();

      Code:

      0: aload_0

      1: invokespecial #1 // Method java/lang/Object."":()V

      4: aload_0

      5: new #2 // class java/lang/Object

      8: dup

      9: invokespecial #1 // Method java/lang/Object."":()V

      12: putfield #3 // Field object:Ljava/lang/Object;

      15: return

      public int add();

      Code:

      0: iconst_1

      1: istore_1

      2: iconst_2

      3: istore_2

      4: iload_1

      5: iload_2

      6: iadd

      7: bipush 100

      9: imul

      10: istore_3

      11: iload_3

      12: ireturn

      public static void main(java.lang.String[]) throws java.lang.InterruptedException;

      Code:

      0: new #4 // class com/App

      3: dup

      4: invokespecial #5 // Method "":()V

      7: astore_1

      8: aload_1

      9: invokevirtual #6 // Method add:()I

      12: istore_2

      13: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;

      16: iload_2

      17: invokevirtual #8 // Method java/io/PrintStream.println:(I)V

      20: return

      }


      此時(shí)的jvm指令碼就清晰很多了,大體結(jié)構(gòu)是可以看懂的,類、靜態(tài)變量、構(gòu)造方法、add()方法、main()方法。

      其中方法中的指令還是有點(diǎn)懵,我們舉add()方法來看一下:

      Code:

      0: iconst_1

      1: istore_1

      2: iconst_2

      3: istore_2

      4: iload_1

      5: iload_2

      6: iadd

      7: bipush 100

      9: imul

      10: istore_3

      11: iload_3

      12: iretu


      這幾行代碼就是對應(yīng)的我們代碼中add()方法中的四行代碼。大家都知道越底層的代碼,代碼實(shí)現(xiàn)的行數(shù)越多,因?yàn)樗麜恍﹋ava代碼在運(yùn)行時(shí)底層隱藏的一些細(xì)節(jié)原理。

      那么一樣的,這個jvm指令官方也是有手冊可以查閱的,網(wǎng)上也有很多翻譯版本,大家如果想了解可自行百度。

      執(zhí)行流程

      設(shè)計(jì)代碼中的部分指令含義:

      第一步:壓棧

      將int類型常量1壓入操作數(shù)棧

      0: iconst_1

      就是將1壓入操作數(shù)棧

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      更正:執(zhí)行流程過程中灰色背景“操作數(shù)?!睉?yīng)改為“局部變量表”(!?。。。。。。。。。?。

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      第二步:存儲

      將int類型值存入局部變量1

      1: istore_1

      局部變量1,在我們代碼中也就是第一個局部變量a,先給a在局部變量表中分配內(nèi)存,然后將int類型的值,也就是目前唯一的一個1存入局部變量a

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      第三步:賦值

      這兩行代碼就和前兩行類似了。

      2: iconst_2

      3: istore_2

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      第四步:裝載

      從局部變量2中裝載int類型值

      4: iload_1

      5: iload_2

      這兩個代碼是將局部變量1和2,也就是a和b的值裝載到操作數(shù)棧中

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      第五步:加法

      執(zhí)行int類型的加法

      6: iadd

      iadd指令一執(zhí)行,會將操作數(shù)棧中的1和2依次從棧底彈出并相加,然后把運(yùn)算結(jié)果3在壓入操作數(shù)棧底。

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      第六步:壓棧

      將一個8位帶符號整數(shù)壓入棧

      7: bipush 100

      這個指令就是將100壓入棧

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      第七步:乘法

      執(zhí)行int類型的乘法

      9: imul

      這里就類似上面的加法了,將3和100彈出棧,把結(jié)果300壓入棧

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      第八步:壓棧

      將將int類型值存入局部變量3

      10: istore_3

      這里大家就不陌生了吧,和第二步第三步是一樣的,將300存入局部變量3,也就是c

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      第九步:裝載

      從局部變量3中裝載int類型值

      11: iload_3

      從局表變量3加載到操作數(shù)棧

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      第十步:返回

      返回int類型值

      12: ireturn

      我們add方法是被main方法中調(diào)用的,所以通過方法出口返回到mian方法中result變量存儲方法出口說白了不就是方法執(zhí)行完了之后要出到哪里,那么我們知道上面add()方法執(zhí)行完之后應(yīng)該回到main()方法第三行那么當(dāng)main()方法調(diào)用add()的時(shí)候,add()棧幀中的方法出口就存儲了當(dāng)前要回到的位置,那么當(dāng)add()方法執(zhí)行完之后,會根據(jù)方法出口中存儲的相關(guān)信息回到main()方法的相應(yīng)位置??次覉D中的紅線

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      棧堆關(guān)系

      main方法中除了result變量還有一個app變量,app變量指向的是一個對象。那對象是怎么存儲的?這兒要在說下局表變量表結(jié)構(gòu):基本類型和引用類型(Java叫引用C C++叫指針)

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      關(guān)系就是:

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      通過引用在棧中的app變量引用堆中的App對象

      總結(jié)

      講到這兒相信大家對JVM棧執(zhí)行原理是不是熟悉了?如果覺得不錯歡迎點(diǎn)贊評論。

      程序員:JVM虛擬機(jī)-棧執(zhí)行原理深入詳解

      推薦閱讀:青海信息港