<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
StackOverflowError可能會讓Java開發人員感到惱火,因為它是我們可能遇到的最常見的執行時錯誤之一。 在本文中,我們將通過檢視各種程式碼範例以及如何處理它來了解此錯誤是如何發生的。 Stack Frames和StackOverflowerError的發生方式 讓我們從基礎開始。呼叫方法時,將在呼叫堆疊上建立新的堆疊幀(stack frame)。該堆疊框架包含被呼叫方法的引數、其區域性變。
StackOverflowError 可能會讓Java開發人員感到惱火,因為它是我們可能遇到的最常見的執行時錯誤之一。
在本文中,我們將通過檢視各種程式碼範例以及如何處理它來了解此錯誤是如何發生的。
讓我們從基礎開始。呼叫方法時,將在呼叫堆疊上建立新的堆疊幀(stack frame)。該堆疊框架包含被呼叫方法的引數、其區域性變數和方法的返回地址,即在被呼叫方法返回後應繼續執行方法的點。
堆疊幀的建立將繼續,直到到達巢狀方法中的方法呼叫結束。
在此過程中,如果JVM遇到沒有空間建立新堆疊幀的情況,它將丟擲 StackOverflower
錯誤。
JVM遇到這種情況的最常見原因是未終止/無限遞迴——StackOverflowerr的Javadoc描述提到,錯誤是由於特定程式碼段中的遞迴太深而引發的。
然而,遞迴並不是導致此錯誤的唯一原因。在應用程式不斷從方法內呼叫方法直到堆疊耗盡的情況下,也可能發生這種情況。這是一種罕見的情況,因為沒有開發人員會故意遵循糟糕的編碼實踐。另一個罕見的原因是方法中有大量區域性變數。
當應用程式設計為類之間具有迴圈關係時,也可以丟擲StackOverflowError。在這種情況下,會重複呼叫彼此的建構函式,從而引發此錯誤。這也可以被視為遞迴的一種形式。
另一個引起此錯誤的有趣場景是,如果一個類在同一個類中作為該類的範例變數範例化。這將導致一次又一次(遞迴)呼叫同一類別建構函式,最終導致堆疊溢位錯誤。
在下面所示的範例中,由於意外遞迴,開發人員忘記為遞迴行為指定終止條件,將丟擲StackOverflowError錯誤:
public class UnintendedInfiniteRecursion { public int calculateFactorial(int number) { return number * calculateFactorial(number - 1); } }
在這裡,對於傳遞到方法中的任何值,在任何情況下都會引發錯誤:
public class UnintendedInfiniteRecursionManualTest { @Test(expected = <a href="https://javakk.com/tag/stackoverflowerror" rel="external nofollow" rel="external nofollow" title="檢視更多關於 StackOverflowError 的文章" target="_blank">StackOverflowError</a>.class) public void givenPositiveIntNoOne_whenCalFact_thenThrowsException() { int numToCalcFactorial= 1; UnintendedInfiniteRecursion uir = new UnintendedInfiniteRecursion(); uir.calculateFactorial(numToCalcFactorial); } @Test(expected = StackOverflowError.class) public void givenPositiveIntGtOne_whenCalcFact_thenThrowsException() { int numToCalcFactorial= 2; UnintendedInfiniteRecursion uir = new UnintendedInfiniteRecursion(); uir.calculateFactorial(numToCalcFactorial); } @Test(expected = StackOverflowError.class) public void givenNegativeInt_whenCalcFact_thenThrowsException() { int numToCalcFactorial= -1; UnintendedInfiniteRecursion uir = new UnintendedInfiniteRecursion(); uir.calculateFactorial(numToCalcFactorial); } }
但是,在下一個範例中,指定了終止條件,但如果將值 -1
傳遞給 calculateFactorial()
方法,則永遠不會滿足終止條件,這會導致未終止/無限遞迴:
public class InfiniteRecursionWithTerminationCondition { public int calculateFactorial(int number) { return number == 1 ? 1 : number * calculateFactorial(number - 1); } }
這組測試演示了此場景:
public class InfiniteRecursionWithTerminationConditionManualTest { @Test public void givenPositiveIntNoOne_whenCalcFact_thenCorrectlyCalc() { int numToCalcFactorial = 1; InfiniteRecursionWithTerminationCondition irtc = new InfiniteRecursionWithTerminationCondition(); assertEquals(1, irtc.calculateFactorial(numToCalcFactorial)); } @Test public void givenPositiveIntGtOne_whenCalcFact_thenCorrectlyCalc() { int numToCalcFactorial = 5; InfiniteRecursionWithTerminationCondition irtc = new InfiniteRecursionWithTerminationCondition(); assertEquals(120, irtc.calculateFactorial(numToCalcFactorial)); } @Test(expected = StackOverflowError.class) public void givenNegativeInt_whenCalcFact_thenThrowsException() { int numToCalcFactorial = -1; InfiniteRecursionWithTerminationCondition irtc = new InfiniteRecursionWithTerminationCondition(); irtc.calculateFactorial(numToCalcFactorial); } }
在這種特殊情況下,如果將終止條件簡單地表示為:
public class RecursionWithCorrectTerminationCondition { public int calculateFactorial(int number) { return number <= 1 ? 1 : number * calculateFactorial(number - 1); } }
下面的測試在實踐中顯示了這種情況:
public class RecursionWithCorrectTerminationConditionManualTest { @Test public void givenNegativeInt_whenCalcFact_thenCorrectlyCalc() { int numToCalcFactorial = -1; RecursionWithCorrectTerminationCondition rctc = new RecursionWithCorrectTerminationCondition(); assertEquals(1, rctc.calculateFactorial(numToCalcFactorial)); } }
現在讓我們來看一個場景,其中StackOverflowError錯誤是由於類之間的迴圈關係而發生的。讓我們考慮 ClassOne
和 ClassTwo
,它們在其建構函式中相互範例化,從而產生迴圈關係:
public class ClassOne { private int oneValue; private ClassTwo clsTwoInstance = null; public ClassOne() { oneValue = 0; clsTwoInstance = new ClassTwo(); } public ClassOne(int oneValue, ClassTwo clsTwoInstance) { this.oneValue = oneValue; this.clsTwoInstance = clsTwoInstance; } }
public class ClassTwo { private int twoValue; private ClassOne clsOneInstance = null; public ClassTwo() { twoValue = 10; clsOneInstance = new ClassOne(); } public ClassTwo(int twoValue, ClassOne clsOneInstance) { this.twoValue = twoValue; this.clsOneInstance = clsOneInstance; } }
現在讓我們假設我們嘗試範例化ClassOne,如本測試中所示:
public class CyclicDependancyManualTest { @Test(expected = StackOverflowError.class) public void whenInstanciatingClassOne_thenThrowsException() { ClassOne obj = new ClassOne(); } }
這最終導致了StackOverflowError錯誤,因為 ClassOne
的建構函式範例化了 ClassTwo
,而 ClassTwo
的建構函式再次範例化了 ClassOne
。這種情況反覆發生,直到它溢位堆疊。
接下來,我們將看看當一個類作為該類的範例變數在同一個類中範例化時會發生什麼。
如下一個範例所示, AccountHolder
將自身範例化為範例變數 JointaCountHolder
:
public class AccountHolder { private String firstName; private String lastName; AccountHolder jointAccountHolder = new AccountHolder(); }
當 AccountHolder
類範例化時,由於建構函式的遞迴呼叫,會引發StackOverflowError錯誤,如本測試中所示:
public class AccountHolderManualTest { @Test(expected = StackOverflowError.class) public void whenInstanciatingAccountHolder_thenThrowsException() { AccountHolder holder = new AccountHolder(); } }
當遇到StackOverflowError堆疊溢位錯誤時,最好的做法是仔細檢查堆疊跟蹤,以識別行號的重複模式。這將使我們能夠定位具有問題遞迴的程式碼。
讓我們研究一下由我們前面看到的程式碼範例引起的幾個堆疊跟蹤。
如果忽略預期的異常宣告,則此堆疊跟蹤由 InfiniteCursionWithTerminationConditionManualTest
生成:
java.lang.StackOverflowError at c.b.s.InfiniteRecursionWithTerminationCondition .calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5) at c.b.s.InfiniteRecursionWithTerminationCondition .calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5) at c.b.s.InfiniteRecursionWithTerminationCondition .calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5) at c.b.s.InfiniteRecursionWithTerminationCondition .calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5)
在這裡,可以看到第5行重複。這就是進行遞迴呼叫的地方。現在只需要檢查程式碼,看看遞迴是否以正確的方式完成。
下面是我們通過執行 CyclicDependancyManualTest
(同樣,沒有預期的異常)獲得的堆疊跟蹤:
java.lang.StackOverflowError at c.b.s.ClassTwo.<init>(ClassTwo.java:9) at c.b.s.ClassOne.<init>(ClassOne.java:9) at c.b.s.ClassTwo.<init>(ClassTwo.java:9) at c.b.s.ClassOne.<init>(ClassOne.java:9)
該堆疊跟蹤顯示了在迴圈關係中的兩個類中導致問題的行號。ClassTwo的第9行和ClassOne的第9行指向建構函式中試圖範例化另一個類的位置。
徹底檢查程式碼後,如果以下任何一項(或任何其他程式碼邏輯錯誤)都不是錯誤的原因:
嘗試增加堆疊大小是個好主意。根據安裝的JVM,預設堆疊大小可能會有所不同。
-Xss
標誌可以用於從專案的設定或命令列增加堆疊的大小。
在本文中,我們仔細研究了StackOverflower錯誤,包括Java程式碼如何導致它,以及我們如何診斷和修復它。
與本文相關的原始碼可以在GitHub上找到: https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-exceptions
到此這篇關於Java中的StackOverflowError錯誤的文章就介紹到這了,更多相關Java StackOverflowError錯誤內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45