無論你是跟同事、同學、上下級、同行、或者面試官討論技術問題的時候,很容易卷入JVM大型撕逼現(xiàn)場。為了能夠讓大家從大型撕逼現(xiàn)場中脫穎而出,最近我苦思冥想如何把知識點盡可能呈現(xiàn)的容易理解,方便記憶。于是就開啟了這一系列文章的編寫。為了讓JVM相關知識點能夠形成一個體系,arthinking將編寫整理一系列的專題,以盡量以圖片的方式描述相關知識點,并且最終把所有相關知識點串成了一張圖。持續(xù)更新中,歡迎大家閱讀。有任何錯落之處也請您高抬貴手幫忙指正,感謝!
從Java運行時數(shù)據(jù)區(qū)域是如何工作的這篇文章我們知道,線程中的 棧結構如下:
每個棧幀包含:本地變量表,操作數(shù)棧,動態(tài)鏈接,返回地址等東西...
也就是說棧調(diào)用深度越大,棧幀就越多,就越耗內(nèi)存。
1、測試案例
1.1、測試線程棧大小對棧深度的影響
下面我們用一個測試例子來說明:
有如下遞歸方法:
public class StackTest {
private int count = 0;
public void recursiveCalls(String a){
count++;
System.out.println("stack depth: " + count);
recursiveCalls(a);
}
public void test(){
try {
recursiveCalls("a");
} catch (Exception e) {
System.out.println(e);
}
}
public static void main(String[] args) {
new StackTest().test();
}
}
我們設置啟動參數(shù)
-Xms256m -Xmx256m -Xmn128m -Xss256k
輸出內(nèi)容:
stack depth: 1556
Exception in thread "main" java.lang.StackOverflowError
at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)
可以發(fā)現(xiàn),棧深度為1556的時候,就報 StackOverflowError了。
接下來我們調(diào)整-Xss線程棧大小為 512k,輸出內(nèi)容:
stack depth: 3249
Exception in thread "main" java.lang.StackOverflowError
at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
發(fā)現(xiàn)棧深度變味了3249,說明了:
隨著線程棧的大小越大,能夠支持越多的方法調(diào)用,也即是能夠存儲更多的棧幀。
1.2、測試方法參數(shù)個對棧深度的影響
這里我們固定設置-Xss為256k。
我們知道此時的深度為:1556。
接下來我們給方法添加參數(shù):
public class StackTest {
private int count = 0;
public void recursiveCalls(String a){
count++;
System.out.println("stack depth: " + count);
recursiveCalls(a);
}
public void test(){
try {
recursiveCalls("a");
} catch (Exception e) {
System.out.println(e);
}
}
public static void main(String[] args) {
new StackTest().test();
}
}
為何要添加參數(shù)呢,因為添加參數(shù)之后,棧幀中的本地變量表就會增加內(nèi)容,我們可以嘗試使用以下命令查看下Class文件的匯編指令:
javap -v StackTest.class
可以發(fā)現(xiàn)recursiveCalls方法的本地變量表的確增加了,對應方法的入?yún)?a:
LocalVariableTable:
Start Length Slot Name Signature
0 44 0 this Lcom/itzhai/jvm/stacks/StackTest;
0 44 1 a Ljava/lang/String;
這個時候我們在執(zhí)行程序看看結果:
stack depth: 1318
Exception in thread "main" java.lang.StackOverflowError
at java.nio.Buffer.<init>(Buffer.java:201)
可以發(fā)現(xiàn),棧深度由原來的1556編程了1318。
可以得出結論:
局部變量表內(nèi)容越多,那么棧幀就越大,棧深度就越小。
2、結論
- 隨著線程棧的大小越大,能夠支持越多的方法調(diào)用,也即是能夠存儲更多的棧幀;
- 局部變量表內(nèi)容越多,那么棧幀就越大,棧深度就越小。
我們在評審寫代碼的時候,發(fā)現(xiàn)了堆棧溢出,可以查看下對應類的本地變量表,是不是太多了,可不可以優(yōu)化下代碼,或者加大下線程棧的大小,以增加棧的深度。
知道了這個,我們下次面試別人的時候也可以問問對方看看了,嘿嘿。
References
What is the maximum depth of the java call stack?
本文為arthinking基于相關技術資料和官方文檔撰寫而成,確保內(nèi)容的準確性,如果你發(fā)現(xiàn)了有何錯漏之處,煩請高抬貴手幫忙指正,萬分感激。
大家可以關注我的博客:itzhai.com 獲取更多文章,我將持續(xù)更新后端相關技術,涉及JVM、Java基礎、架構設計、網(wǎng)絡編程、數(shù)據(jù)結構、數(shù)據(jù)庫、算法、并發(fā)編程、分布式系統(tǒng)等相關內(nèi)容。
如果您覺得讀完本文有所收獲的話,可以關注我的賬號,或者點贊的,您的支持就是我寫作的動力!關注我的公眾號,及時獲取最新的文章。
本文作者: arthinking
博客鏈接: https://www.itzhai.com/jvm/how-stack-frame-can-a-thread-hold.html
版權聲明:
BY-NC-SA許可協(xié)議:創(chuàng)作不易,如需轉載,請務必附加上博客鏈接,謝謝!