先來張圖: 一、程式計數器:指向當前線程正在執行的位元組碼指令的地址(或者說行號)。每個線程都有一個自己的計數器。 為什麼指向正在執行的位元組碼指令的地址?因為CPU會切換線程執行,當前線程可能會被掛起,被掛起的時候當前指令可能沒執行完成,待CPU重新調度到該線程時,需要知道當前線程執行到哪了。 二、虛 ...
先來張圖:
一、程式計數器:指向當前線程正在執行的位元組碼指令的地址(或者說行號)。每個線程都有一個自己的計數器。
為什麼指向正在執行的位元組碼指令的地址?因為CPU會切換線程執行,當前線程可能會被掛起,被掛起的時候當前指令可能沒執行完成,待CPU重新調度到該線程時,需要知道當前線程執行到哪了。
二、虛擬機棧:存儲當前線程運行方法所需要的數據,指令和返回地址。(---->棧---->數據結構----->存儲數據---->存什麼數據?)
虛擬機描述的是java方法執行的記憶體模型:每個方法執行時會創建一個棧幀。棧幀的結構大致如下:(網上找來的圖)
1.局部變數表:用於存放方法參數和方法內部定義的局部變數。(大小在編譯時已經確定)
局部變數表以變數槽(Slot)為 最小單位,boolean,byte,char,short,float,reference或returnAddress占一個Slot,
long ,double占兩個Slot。
舉個例子:(某視頻上的)
1 public class HelloWorldDemo { 2 // 常量、靜態變數 3 private final int i = 0; 4 private static int k = 0; 5 //成員變數 6 private Object obj=new Object(); 7 private int sss=0; 8 9 //局部變數 10 public void methodOne(int i) { 11 int j=0; 12 int sum=i+j; 13 Object acb=obj; 14 long start = System.currentTimeMillis(); 15 methodTwo(); 16 return; 17 } 18 19 public static void methodTwo() { 20 methodTwo(); 21 } 22 public static void main(String[] args) { 23 methodTwo(); 24 } 25 }
使用javap -c -v 命令反編譯其class文件,可以看到methodOne方法各變數在局部變數表的位置:
可以看出:this在0位置,i,j,sum等分別在1,2,3位置。(非靜態方法0位置是this)
2.操作數棧:通常稱為操作棧(後入先出),棧的最大深度在編譯時已確定。
網上搜索瞭解methodOne大概執行流程:
public void methodOne(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=7, args_size=2
0: iconst_0 //將int類型常量0壓入棧(虛擬機棧)
1: istore_2 //將int類型值存入局部變數2 (即程式中的j)(此時0會出棧)
2: iload_1 //從局部變數表1中裝載int類型值(即i的值)(i值入棧)
3: iload_2 //從局部變數表2中裝載int類型值(即j的值)(j值入棧)
4: iadd //執行add操作
5: istore_3 //將結果存入局部變數表3 (即sum)(結果出棧)
6: aload_0 // 將第一個引用類型本地變數推送至棧頂(非靜態方法是this,靜態方法是第一個引用類型變數)
7: getfield #25 // Field obj:Ljava/lang/Object; //將棧頂的指定的對象的第25個實例域(Field)的值(6到7不是很明白,this的值,按流程aload_0應該指的是obj這個對象)
(這個值可能是引用,這裡就是引用)壓入棧頂
10: astore 4 //將棧頂的值存入局部變數4 (即acb)
12: invokestatic #33 // Method java/lang/System.currentTimeMillis:()J //調用類的static方法
15: lstore 5 //將棧頂long型數值存入局部變數表5中(即start)
17: invokestatic #39 // Method methodTwo:()V //調用本類static方法methodTwo()
20: return
LineNumberTable:
line 13: 0
line 14: 2
line 15: 6
line 16: 12
line 17: 17
line 18: 20
LocalVariableTable:
Start Length Slot Name Signature
0 21 0 this Lcom/thomas/jvm/HelloWorldDemo;
0 21 1 i I
2 19 2 j I
6 15 3 sum I
12 9 4 acb Ljava/lang/Object;
17 4 5 start J
3.動態鏈接:運行時才確定真正的鏈接。比如在類內聲明介面類型的變數 private IUSB usb;在方法內調用其do方法時,實際調用的是其具體某個實現類的do方法,這是在運行時才需要確定的,假如方法不執行就不必知道。
4.返回地址:出棧後去哪?返回地址確定
遞歸調用時有多少個棧幀?N個 遞歸會導致StackOverflowError
三、本地方法棧:描述的是本地方法出棧入棧的過程,類比於虛擬機棧。native方法:簡單的說,就是一個java調用非java代碼的介面。
四、方法區:存儲類信息,常量(1.7)靜態變數,JIT(即時編譯器)編譯後的代碼(動態代理,在jvm運行時生成的代碼,也要載入到記憶體中)。
五、Heap(堆):存放對象實例。這裡涉及到JVM記憶體模型(JMM),下一篇。