『StabilityGuide』是阿里多位阿里技術工程師共同發起的穩定性領域的知識庫開源項目,涵蓋性能壓測、故障演練、JVM、應用容器、服務框架、流量調度、監控、診斷等多個技術領域,以更結構化的方式來打造穩定性領域的知識庫,歡迎您的加入。html
@GitHub :https://github.com/StabilityMan/StabilityGuidejava
每個 JVM 線程都擁有一個私有的 JVM 線程棧,用於存放當前線程的 JVM 棧幀(包括被調用函數的參數、局部變量和返回地址等)。若是某個線程的線程棧空間被耗盡,沒有足夠資源分配給新建立的棧幀,就會拋出 java.lang.StackOverflowError 錯誤。git
首先給出一個簡單的程序調用代碼示例,以下所示:github
public class SimpleExample { public static void main(String args[]) { a(); } public static void a() { int x = 0; b(); } public static void b() { Car y = new Car(); c(); } public static void c() { float z = 0f; } }
當 main() 方法被調用後,執行線程按照代碼執行順序,將它正在執行的方法、基本數據類型、對象指針和返回值包裝在棧幀中,逐一壓入其私有的調用棧,總體執行過程以下圖所示:緩存
首先,程序啓動後,main() 方法入棧。oracle
而後,a() 方法入棧,變量 x 被聲明爲 int 類型,初始化賦值爲 0。注意,不管是 x 仍是 0 都被包含在棧幀中。框架
接着,b() 方法入棧,建立了一個 Car 對象,並被賦給變量 y。請注意,實際的 Car 對象是在 Java 堆內存中建立的,而不是線程棧中,只有 Car 對象的引用以及變量 y 被包含在棧幀裏。jvm
最後,c() 方法入棧,變量 z 被聲明爲 float 類型,初始化賦值爲 0f。同理,z 仍是 0f 都被包含在棧幀裏。ide
當方法執行完成後,全部的線程棧幀將按照後進先出的順序逐一出棧,直至棧空爲止。函數
如上所述,JVM 線程棧存儲了方法的執行過程、基本數據類型、局部變量、對象指針和返回值等信息,這些都須要消耗內存。一旦線程棧的大小增加超過了容許的內存限制,就會拋出 java.lang.StackOverflowError 錯誤。
下面這段代碼經過無限遞歸調用最終引起了 java.lang.StackOverflowError 錯誤。
public class StackOverflowErrorExample { public static void main(String args[]) { a(); } public static void a() { a(); } }
在這種狀況下,a() 方法將無限入棧,直至棧溢出,耗盡線程棧空間,以下圖所示。
Exception in thread "main" java.lang.StackOverflowError at StackOverflowErrorExample.a(StackOverflowErrorExample.java:10) at StackOverflowErrorExample.a(StackOverflowErrorExample.java:10) at StackOverflowErrorExample.a(StackOverflowErrorExample.java:10) at StackOverflowErrorExample.a(StackOverflowErrorExample.java:10) at StackOverflowErrorExample.a(StackOverflowErrorExample.java:10) at StackOverflowErrorExample.a(StackOverflowErrorExample.java:10) at StackOverflowErrorExample.a(StackOverflowErrorExample.java:10) at StackOverflowErrorExample.a(StackOverflowErrorExample.java:10) at StackOverflowErrorExample.a(StackOverflowErrorExample.java:10)
引起 StackOverFlowError 的常見緣由有如下幾種:
除了程序拋出 StackOverflowError 錯誤之外,還有兩種定位棧溢出的方法:
常見的解決方法包括如下幾種:
線程棧的默認大小依賴於操做系統、JVM 版本和供應商,常見的默認配置以下表所示:
JVM 版本 | 線程棧默認大小 |
---|---|
Sparc 32-bit JVM | 512 kb |
Sparc 64-bit JVM | 1024 kb |
x86 Solaris/Linux 32-bit JVM | 320 kb |
x86 Solaris/Linux 64-bit JVM | 1024 kb |
Windows 32-bit JVM | 320 kb |
Windows 64-bit JVM | 1024 kb |
提示: 實際生產系統中,能夠對程序日誌中的 StackOverFlowError 配置關鍵字告警,一經發現,當即處理。
ARMS —— 阿里雲 APM 產品,支持 StackOverFlowError 異常關鍵字告警
做者信息:夏明,GitHub ID @StabilityMan,花名涯海,阿里雲 ARMS & EagleEye 技術專家,2016 年加入阿里巴巴,一直從事鏈路追蹤和 APM 監控診斷領域的相關工做。
本文爲雲棲社區原創內容,未經容許不得轉載。