關于JVM和OS中的棧幀的區別和內存淺析
剛看了黑馬JVM中的棧幀的講解,感覺和自己理解的棧幀有一定出入,查詢資料研究了一下發現的確有天壤之別,可惜黑馬并沒有講。
故寫下這篇文章鞏固一下,
OS的棧幀:
? OS的棧幀會在調用一個函數時,分配一段內存,存入函數調用的局部變量、參數、返回地址等信息進去,列如下面這一段C程序:
#include<stdio.h>
int add1(int a, int b) {return add2(a,b);
}
int add2(int a, int b) {return a + b;
}
void main(){add1(1,2);
}
首先,程序會把這個代碼從上開始執行
當執行到main函數時,會在內存上分配如下:
這里的返回地址指的是進入這個函數的指令地址,也就是**程序計數器(PC)**寄存器,里面保存的是下一條被執行的語句的位置,因為你調用了函數,PC就得移到函數代碼的地方,為了執行完函數能回到原代碼,就會在函數的棧幀里面保存返回地址,這樣執行完函數就會通過返回地址返回到原本的地方繼續執行。
接著進入add1,add2就顯而易見:
棧幀是用棧來存儲的,遵循先進先出,程序只能操作最頂上的棧幀!
每當執行到一個函數,就會創建一個棧幀,存儲變量和對應返回地址,當執行完畢后,棧幀會被回收
這也就是為什么函數需要預先聲明要用的變量的原因,計算機需要知道你的大小和所需來為你分配合適的棧幀。
你只能使用你棧幀里面的數據,一旦越界訪問就會報錯
總的來說,棧幀在OS里面會保存函數執行的一切信息,包括變量對象,返回地址,各種參數
但對于全局變量,以及類似于C++中的vector這種動態申請空間的,棧幀里面只會存對其的內存地址(可以理解為指針),實際上其處于另一片區域中,具體看程序內存分配的總體圖:
這也被稱為內存五大區:棧區 -> 堆區 -> 全局靜態區 -> 常量區 -> 代碼區
各個區解釋如下:
內核區,是OS的系統文件或其他文件,反正不應該被程序觸碰到,接觸到就會報錯
棧區,也就是上文提到的棧幀存放處,從上到下存儲!
堆區, 存儲程序手動分配的空間,也就是new,vector此類,讀取較慢
全局區,存儲全局變量
常量區,存儲常量
代碼區,存儲原代碼
保留區,4k大小,不作操作
對于32位計算機,全部部分加起來一共有4G
棧區是從上到下使用,堆區是從下到上
緩存區用來作緩沖,雙方都能使用
一旦沖突,便會發生溢出錯誤
以上是OS中棧幀的簡單解釋
JVM的棧幀:
Java這種高級語言的棧幀和OS中的棧幀是兩個東西
本質是JVM抽象出來的,和OS的棧幀沒有關系
但是結構差不多,可以說較為相似
(來自小林coding)
這里的本地方法指的是native方法,不是由java實現的方法!是由C語言實現的來對底層進行交互的方法!
那本地方法棧完全可以套用前文
對于Java虛擬機棧幀,僅和OS棧幀有一點區別:其不會把數據對象存儲在棧幀中,而是只會存儲一個引用!
對象本身是存在堆中的,和OS的棧幀不同
對于static的變量和方法,static方法在內存上的存儲方式其實和非static沒有多大區別,都是同樣的創建棧幀調用,只是會少一個this指針,綁定在static中,不能實現多態,可以直接調用
而static變量則會直接保存在方法區,不像非staic變量存儲在堆里面
堆是 JVM 中最大的一塊內存區域,被所有線程共享,在虛擬機啟動時創建,用于存放對象實例。
程序計數器(PC)效果和OS相同,都是存儲下一條該執行的語句的地址
元空間用于存儲已被虛擬機加載的類信息、常量、靜態變量等數據
直接內存不屬于 JVM 運行時數據區的一部分,通過 NIO 類引入,是一種堆外內存,可以顯著提高 I/O 性能。
總的來說,JVM和OS的內存管理結構上其實并沒有很大區別,只是需要注意,JVM的內存是對OS的內存的一次抽象,是間接實現的,不是像OS那樣通過匯編直接實現.
所以說既然不是直接實現的,那就必然會對性能有影響,這也是java性能不如C的原因之一
棧幀也是對OS的棧幀的模擬,區別在于不會把所有對象都放在棧幀中,而是放在堆中,這是和OS的區別