一、虛擬機棧(VM Stack)
1.1)什么是虛擬機棧
虛擬機棧是用於描述java方法執行的內存模型。
每個java方法在執行時,會創建一個“棧幀(stack frame)”,棧幀的結構分為“局部變量表、操作數棧、動態鏈接、方法出口”幾個部分(具體的作用會在字節碼執行引擎章節中講到,這里只需要了解棧幀是一個方法執行時所需要數據的結構)。我們常說的“堆內存、棧內存”中的“棧內存”指的便是虛擬機棧,確切地說,指的是虛擬機棧的棧幀中的局部變量表,因為這里存放了一個方法的所有局部變量。
方法調用時,創建棧幀,並壓入虛擬機棧;方法執行完畢,棧幀出棧並被銷毀,如下圖所示:

1.2)虛擬機棧的特點
虛擬機棧是線程隔離的,即每個線程都有自己獨立的虛擬機棧。
1.3)虛擬機棧的StackOverflowError
若單個線程請求的棧深度大於虛擬機允許的深度,則會拋出StackOverflowError(棧溢出錯誤)。
JVM會為每個線程的虛擬機棧分配一定的內存大小(-Xss參數),因此虛擬機棧能夠容納的棧幀數量是有限的,若棧幀不斷進棧而不出棧,最終會導致當前線程虛擬機棧的內存空間耗盡,典型如一個無結束條件的遞歸
函數調用,代碼見下:
1 /** 2 * java棧溢出StackOverFlowError 3 * JVM參數:-Xss128k 4 * Created by chenjunyi on 2018/4/25. 5 */ 6 public class JavaVMStackSOF { 7 8 private int stackLength = -1; 9 10 //通過遞歸調用造成StackOverFlowError 11 public void stackLeak() { 12 stackLength++; 13 stackLeak(); 14 } 15 16 public static void main(String[] args) { 17 JavaVMStackSOF oom = new JavaVMStackSOF(); 18 try { 19 oom.stackLeak(); 20 } catch (Throwable e) { 21 System.out.println("Stack length:" + oom.stackLength); 22 e.printStackTrace(); 23 } 24 } 25 26 }
設置單個線程的虛擬機棧內存大小為128K,執行main方法后,拋出了StackOverflow異常
1 Stack length:983 2 java.lang.StackOverflowError 3 at com.manayi.study.jvm.chapter2._02_JavaVMStackSOF.stackLeak(_02_JavaVMStackSOF.java:14) 4 at com.manayi.study.jvm.chapter2._02_JavaVMStackSOF.stackLeak(_02_JavaVMStackSOF.java:15) 5 at com.manayi.study.jvm.chapter2._02_JavaVMStackSOF.stackLeak(_02_JavaVMStackSOF.java:15) 6 ······
1.4)虛擬機棧的OutOfMemoryError
不同於StackOverflowError,OutOfMemoryError指的是當
整個虛擬機棧內存耗盡,並且無法再申請到新的內存時拋出的異常。
JVM未提供設置整個虛擬機棧占用內存的配置參數。虛擬機棧的最大內存大致上等於“JVM進程能占用的最大內存(依賴於具體操作系統) - 最大堆內存 - 最大方法區內存 - 程序計數器內存(可以忽略不計) - JVM進程本身消耗內存”。當虛擬機棧能夠使用的最大內存被耗盡后,便會拋出OutOfMemoryError,可以通過不斷開啟新的線程來模擬這種異常,代碼如下:
1 ** 2 * java棧溢出OutOfMemoryError 3 * JVM參數:-Xss2m 4 * Created by chenjunyi on 2018/4/25. 5 */ 6 public class JavaVMStackOOM { 7 8 private void dontStop() { 9 while (true) { 10 } 11 } 12 13 //通過不斷的創建新的線程使Stack內存耗盡 14 public void stackLeakByThread() { 15 while (true) { 16 Thread thread = new Thread(() -> dontStop()); 17 thread.start(); 18 } 19 } 20 21 public static void main(String[] args) { 22 JavaVMStackOOM oom = new _03_JavaVMStackOOM(); 23 oom.stackLeakByThread(); 24 } 25 26 }
設置單個線程虛擬機棧的占用內存為2m並不斷生成新的線程,最終虛擬機棧無法申請到新的內存,拋出異常:
1 Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
二、本地方法棧(Native Method Stack)
本地方法棧的功能和特點類似於虛擬機棧,均具有線程隔離的特點以及都能拋出StackOverflowError和OutOfMemoryError異常。
不同的是,本地方法棧服務的對象是JVM執行的native方法,而虛擬機棧服務的是JVM執行的java方法。如何去服務native方法?native方法使用什么語言實現?怎么組織像棧幀這種為了服務方法的數據結構?虛擬機規范並未給出強制規定,因此不同的虛擬機實可以進行自由實現,我們常用的HotSpot虛擬機選擇合並了虛擬機棧和本地方法棧。