JVM 線程中方法執行過程


 本章節內容參考:《深入理解Java虛擬機》

運行時數據區:

 

 本次只介紹用於程序運行的線程私有的內存模型。   

                虛擬機棧(FILO):java方法執行的內存模型。

                    棧幀(線程執行的一個方法的內存模型,每調用一個方法,壓入一個棧幀)

                              局部變量表:編譯器可知的8種基本類型、reference類型、returnAddress類型

                              操作數棧:一個用於計算的臨時數據存儲區(明顯,此棧是為了存放要操作的數據用的)

                              動態鏈接:支持java多態

                              返回地址:方法結束的地方。return/Exception        

                本地方法棧:Native方法執行的內存模型。

                程序計數器:這個計數器記錄的是正在執行的虛擬機字節碼指令的地址(如果線程正在執行的是一個java方法)。 字節碼解釋器工作時,就是通過改變這個計數器的值來選取需要執行的字節碼                                         指令(分支,循環,跳轉、異常處理、線程恢復)

 

線程中,方法A調用方法B。

 

線程的執行的過程:

    1、線程開始,分配虛擬機棧大小(JVM參數 -Xss:大小,1.5+默認1M),

         2、執行方法A時,創建一個棧幀A壓入虛擬機棧頂,根據程序計數器中的記錄的下一個要執行的字節碼指令的地址,找到並執行指令(將要操作的數據壓入操作數棧棧頂,將操作結果放入局部變量表中,詳細過程參照下面“合代碼演示”部分)。

        3、中間調用方法B,則創建棧幀B,接着執行方法B的指令,直到方法B結束(遇到方法返回的字節碼指令或異常),B棧幀出棧,如果有返回數據,將返回數據壓入棧幀A的操作數棧頂,方法A接着執行。 

  4、方法A執行結束,彈出棧幀A,虛擬機棧中再無棧幀,此線程結束。 

 

為更形象的理解,結合代碼演示(操作數棧和局部變量表):

源碼:

25.      public void add() {

26.           int a = 3;

27.           int b = 4;

28.           int c = a + b;

29.     }

javap結果:

public void add();

    descriptor: ()V

    flags: ACC_PUBLIC

    Code:

  //操作數棧最大深度2,局部變量4, 方法入參1(this + 真正的入參, 如果是方法名add(int a, int b),args_size = 1(this) + 2(a和b) = 3)

       stack=2,   locals=4, args_size=1

         0: iconst_3     // 將int類型常量3壓入操作數棧頂

         1: istore_1     // 將操作數棧頂的數據彈出,存入局部變量表索引1

         2: iconst_4     // 將int類型常量4壓入操作數棧棧頂

         3: istore_2     // 將操作數棧頂的數據彈出,存入局部變量表索引2

         4: iload_1      // 將局部變量表索引為1的數據加入到操作數棧頂

         5: iload_2      // 將局部變量表索引為2的數據加入到操作數棧頂

         6: iadd          // 將棧中2個數相加,將結果入棧頂 

         7: istore_3     // 將棧頂結果彈出,存入局部變量表索引3

         8: return

      LineNumberTable:

        line 26: 0 //java文件代碼第26行對應開始指令0

        line 27: 2 //java文件代碼第26行對應開始指令2

        line 28: 4 //java文件代碼第26行對應開始指令4

        line 29: 8 //java文件代碼第26行對開始應指令8

      LocalVariableTable:  // 局部變量表,4個局部變量,this、a、b、c

        Start  Length  Slot  Name   Signature

            0       9     0  this   LCongoPengYuyan;         

            2       7     1     a   I

            4       5     2     b   I

            8       1     3     c   I

 

一些思考:

  我們都知道,操作數棧存放的數據是(int、long、float、double、reference、returnType)這些類型,

  reference指像的對象是在堆里面,堆是共享的,既然是所有線程共享的,為啥在多線程中,數據會不一致呢,線程A將對象O的某個屬性改了,而線程B拿到O的屬性的值還是未改變的?java還推出了統一的JMM模型。

       其實,CPU執行的時候,是要將內存中的數據,加載到CPU緩存(寄存器等),多線程的時候,數據首先在寄存器中修改,改完后重新刷入內存中。解決多線程數據可見性的問題,java提供了Volatile關鍵字,

  它的實現原理,對象操作后面加入Lock匯編指令。A線程在修改操作后,強制刷入主存,然后通知執行B線程的CPU,你CPU緩存中的值是失效,不能用了,要用請從主存中重新獲取。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM