公司最近買了一套老代碼,在測試環境部署的時候發生了nested exception is java.lang.StackOverflowError
的異常,當時看到這個異常首先想到是棧內存溢出,網上給出的解決辦法就是加棧內存大小就行。趁着這個機會也瞭解一下什麼是Java虛擬機棧。java
咱們想要解決StackOverflowError
問題就得了解其內部工做機制,首先咱們須要瞭解Java虛擬機棧是什麼。瞭解Java虛擬機棧以前咱們先看一段代碼。設計模式
public class TestStack {
public static void main(String[] args) {
a();
}
public static void a(){
int a = 10;
b();
}
public static void b(){
int b = 10;
}
}
複製代碼
上面的一段代碼很是簡單,總體流程就是tomcat
如今讓咱們瞭解上面一段簡單流程狀況下棧的動做流程,首先咱們須要知道棧是每一個線程獨有的,而存放在棧裏面的叫作棧幀,每進入一個方法就會有一個棧幀入棧的動做,即下面的黃色方塊就是棧幀。每一個棧幀裏面存放的是局部變量表(例如此時的int a=10 ,a的值就存放在局部變量表中)、操做數棧、動態連接、方法出口等等。bash
局部變量表中存放的是編譯期可知的各類基本數據類型(boolean、byte、char、short、int、float、double、long)、對象引用(是指向存放的堆中實例對象的地址)測試
當每一個方法執行完畢之後,就會將此棧幀彈出棧。spa
上面咱們知道了每一個線程都有本身獨有的棧空間,而棧幀中存放的私有的變量、對象地址引用、返回值都佔用這內存空間,若是線程棧的大小超過了規定的大小,那麼就會拋出StackOverflowError
。例如我將上面代碼稍微改一下線程
public class TestStack {
public static void main(String[] args) {
a();
}
public static void a(){
b();
}
public static void b(){
a();
}
}
複製代碼
讓他一直循環調用,而且設置每一個線程棧大小爲160KXss160k
,就會發現不斷的加入棧幀,總會達到棧的最大值的,就會拋出StackOverflowError
設計
首先回到咱們開頭的問題上,因爲是買過來的代碼,因此須要知道引發StackOverflowError
的是代碼問題仍是設置的棧內存小了。通過排查看到有一塊驗證信息的代碼中用了責任鏈設計模式,在xml中設置的引用鏈條長達五十多個。所以在初始化的時候若是設置的棧內存不夠大的話那麼就會發生StackOverflowError
。code
因此就直接增大棧內存大小就好了,咱們使用的是公司給提供的Tomcat包部署的項目,經網上查詢在Tomcat中設置JVM參數只須要在catalina.sh
中設置JAVA_OPTS="$JAVA_OPTS -Xss500k"
參數便可,因而設置了此參數後又從新啓動,發現仍是StackOverflowError
錯誤。cdn
而後通過ps -ef |grep tomcat
查看發現設置的棧大小沒有生效。因而在Tomcat目錄中grep -r "Xss" *
查找看是哪裏配置的棧大小,最後發現是在setenv.sh
文件中配置JVM信息,而在setenv.sh
文件中配置的是CATALINA_OPTS="$CATALINA_OPTS -Xss256k"
。
配置只是該Tomcat使用的環境變量,使用
CATALINA_OPTS
,而若是配置其餘Java應用程序也要使用的環境變量,好比JBoss,在JAVA_OPTS
中設置
通過試驗發現若是在配置文件中出現了相同的配置信息。例如
CATALINA_OPTS="$CATALINA_OPTS -Xss300k"
JAVA_OPTS="$JAVA_OPTS -Xss200k"
複製代碼
哪怕JAVA_OPTS
是在後面,那麼也是以CATALINA_OPTS
所配置的爲準,若是兩個都是
CATALINA_OPTS="$CATALINA_OPTS -Xss300k"
CATALINA_OPTS ="$CATALINA_OPTS -Xss200k"
複製代碼
那麼就以出現的前後順序,後出現的配置生效。隨後在setenv.sh
文件中將棧空間大小增大問題也就隨之解決了。