一篇聊聊 JVM 系列之虛擬機棧
?今天繼續給大家分享JVM系列的相關知識,今天介紹一下虛擬機棧的介紹。
1、虛擬機棧的概念
虛擬機棧也稱為Java棧,Java每個main方法被執行的時候,JVM都會同步創建一個棧幀(Stack Frame),通過存儲局部變量表、操作數棧、動態鏈接、方法出口等信息來支撐和完成方法的執行。棧幀就是虛擬機棧中的子單位。棧其實只有入棧和出棧兩種操作。
棧的操作
入棧:每一次方法調用都會有一個對應的棧幀被壓入棧中,也成為壓棧。出棧:方法調用結束后,彈出,也成為彈棧。
2、虛擬機棧的特點
- 先進后出的原則。
- 線程私有的,它的生命周期和線程保持一致,隨線程而生,隨線程而滅。
- 線程請求的棧深度大于虛擬機所允許的最大深度,會拋出StackOverflowError棧溢出異常。
- 虛擬機??梢詣討B擴展,如果擴展的時候無法申請得到到足夠的內存,就會拋出OutOfMemoryError異常。
運行時常見的異常
- NullPointerException - 空指針引用異常
- ClassCastException - 類型強制轉換異常
- IllegalArgumentException - 傳遞不合法參數異常
- ArithmeticException - 算術計算異常
- IndexOutOfBoundsException - 下標越界異常
- NumberFormatException - 數字格式異常
- UnsupportedOperationException - 不支持的操作異常
3、棧幀
Java中每一個方法從調用開始到執行完成的過程,其實都對應著一個棧幀在虛擬機線程里面從入棧到出棧的過程。
4、棧幀的組成
棧幀由局部變量表、操作數棧(Operand Stack)、動態鏈接(Dynamic Linking)、方法返回地址(Return Address)和一些附加信息(對程序調試提供支持的信息)組成。

說明:
- 在活動線程中,只有位于棧頂的棧幀叫做當前棧幀,也是正在執行的方法
- Java執行引擎運行的所有字節碼指令其實都只針對當前棧幀進行操作
4.1 局部變量表
主要存放了編譯期明確的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference 類型,它不是針對對象本身,可能是一個指向對象起始地址的引用指針,也可能是指向一個代表對象的句柄或其他與此對象相關的位置)。
4.2 操作數棧
操作數主要作為方法調用的中轉站使用,主要用來存放Java方法執行過程中產生的中間計算結果和計算過程中產生的臨時變量。
4.3 動態鏈接
動態鏈接主要支撐一個Java方法需要調用其他方法的場景。當 Java 源文件被編譯成字節碼文件時,所有的變量和方法引用都作為符號引用(Symbilic Reference)保存在Class 文件的常量池里面。當一個Java方法要調用其他Java方法,需要將常量池中指向方法的符號引用轉換為其在內存地址中的直接引用。動態鏈接作用:其實就是將符號引用轉換為調用方法的直接引用。
4.4 方法返回地址
Java方法開始執行后,退出這個方法的方式:正常退出、異常退出。
- 正常退出:執行引擎遇到方法返回的字節碼指令,這時候可能會有返回值傳遞給上層的方法調用者。 正常退出時調用PC計數器的值可以作為返回地址。
- 異常退出:在方法執行過程中遇到異常,且異常沒有在方法體內得到處理,返回地址要交給異常處理表來決定如何處理。
說明:方法退出之后,都需要返回到方法被調用的原始位置,程序才能繼續執行。
5、棧的優缺點
優點:棧幀內數據共享:一個棧幀中內存數據共享,不同棧幀之間數據不共享,這樣可以減少內存消耗存儲速度:棧幀存取數據快,僅次于寄存器。
編譯的時候就分配好了內存,運行過程中不需要申請內存大小,節約時間成本。
- 棧是機器提供的數據結構,計算機會分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,效率高。
- 相比較堆來說,訪問效率高。
- 缺點靈活性差:棧在運行過程中,不能動態的去申請內存、程序可能會報錯。
6、內存中棧和堆的對比
棧屬于運行時的單位主要解決程序如何執行的問題,堆屬于存儲的單位主要是用來解決數據的存儲問題。
- 堆是運行時數據區較大的一塊,所以Java的對象基本都放在堆空間。
- 棧主要用來存放基本數據類型的局部變量、引用數據類型的對象的引用

























