Java で StackOverflowError エラーの問題を解決する方法
StackOverflowError の概要
StackOverflowError は、遭遇する可能性のある最も一般的なランタイム エラーの 1 つであるため、Java 開発者にとって迷惑な場合があります。この記事では、さまざまなコード例を見て、このエラーがどのように発生するのか、そしてその対処方法を学びます。 Stack Frames と StackOverflowerError が発生する仕組み 基本的なことから始めましょう。メソッドが呼び出されると、呼び出しスタック上に新しいスタック フレームが作成されます。スタック フレームには、呼び出されたメソッドのパラメータとそのローカル変数が含まれます。
StackOverflowError は、遭遇する可能性のある最も一般的な実行時エラーの 1 つであるため、Java 開発者にとって迷惑な場合があります。
この記事では、さまざまなコード例を見て、このエラーがどのように発生するのか、そしてその対処方法を理解します。
Stack Frames と StackOverflowerError が発生する仕組み
基本的なことから始めましょう。メソッドが呼び出されると、呼び出しスタック上に新しいスタック フレームが作成されます。このスタック フレームには、呼び出されたメソッドのパラメータ、そのローカル変数、およびメソッドの戻りアドレス (呼び出されたメソッドが戻った後にメソッドの実行を継続するポイント) が含まれています。
スタック フレームの作成は、ネストされたメソッド内のメソッド呼び出しの終わりに達するまで続行されます。
このプロセス中に、JVM が新しいスタック フレームを作成するためのスペースがない状況に遭遇すると、StackOverflower
エラーをスローします。
JVM でこれが発生する最も一般的な理由は、終了していない/無限の再帰です。StackOverflowerr の Javadoc の説明では、このエラーは特定のコード部分の深すぎる再帰によって引き起こされると述べられています。
ただし、このエラーの原因は再帰だけではありません。これは、スタックが使い果たされるまでアプリケーションがメソッド内からメソッドを呼び出し続ける状況でも発生する可能性があります。意図的に不適切なコーディング手法に従う開発者はいないため、これはまれな状況です。もう 1 つのまれな理由は、メソッド内のローカル変数が多数であることです。
StackOverflowError は、アプリケーションがクラス間に循環関係を持つように設計されている場合にもスローされる可能性があります。この場合、互いのコンストラクターが繰り返し呼び出されるため、このエラーが発生します。これは再帰の一形態と考えることもできます。
このエラーを引き起こすもう 1 つの興味深いシナリオは、クラスがそのクラスのインスタンス変数として同じクラス内でインスタンス化される場合です。これにより、同じクラスのコンストラクターが何度も (再帰的に) 呼び出され、最終的にはスタック オーバーフロー エラーが発生します。
StackOverflowError running
以下に示す例では、予期しない再帰により、開発者が再帰動作の終了条件を指定するのを忘れたため、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(); } }これは、
ClassOne のコンストラクターが
ClassTwo と
ClassTwo# をインスタンス化するため、最終的に StackOverflowError になります。 ## のコンストラクターはインスタンス化します。 ClassOne
再び。これはスタックからオーバーフローするまで繰り返し発生します。 次に、クラスがそのクラスのインスタンス変数として同じクラス内でインスタンス化された場合に何が起こるかを見ていきます。
次の例に示すように、
AccountHolder はインスタンス変数 JointaCountHolder
として自身をインスタンス化します。<pre class='brush:php;toolbar:false;'>public class AccountHolder {
private String firstName;
private String lastName;
AccountHolder jointAccountHolder = new AccountHolder();
}</pre>
When the
このテストに示すように、クラスをインスタンス化すると、コンストラクターへの再帰呼び出しにより StackOverflowError エラーが発生します。 <pre class='brush:php;toolbar:false;'>public class AccountHolderManualTest {
@Test(expected = StackOverflowError.class)
public void whenInstanciatingAccountHolder_thenThrowsException() {
AccountHolder holder = new AccountHolder();
}
}</pre>
StackOverflowError の解決
StackOverflowError が発生した場合、これを慎重に行うのが最善です。スタック トレースを調べて行番号の繰り返しパターンを特定します。これにより、問題のある再帰が含まれるコードを見つけることができます。
先ほどのコード例によって引き起こされるいくつかのスタック トレースを調べてみましょう。
予期された例外宣言が無視された場合、このスタック トレースは
InfiniteCursionWithTerminationConditionManualTest によって生成されます: <pre class='brush:php;toolbar:false;'>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)</pre>
ここでは、5 行目が繰り返されていることがわかります。ここで再帰呼び出しが行われます。あとはコードをチェックして、再帰が正しい方法で行われているかどうかを確認するだけです。
これは、
CyclicDependancyManualTest を実行することで得られるスタック トレースです (繰り返しますが、例外は予想されません): <pre class='brush:php;toolbar:false;'>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)</pre><p>该堆栈跟踪显示了在循环关系中的两个类中导致问题的行号。ClassTwo的第9行和ClassOne的第9行指向构造函数中试图实例化另一个类的位置。</p>
<p>彻底检查代码后,如果以下任何一项(或任何其他代码逻辑错误)都不是错误的原因:</p>
<ul class=" list-paddingleft-2">
<li><p>错误实现的递归(即没有终止条件)</p></li>
<li><p>类之间的循环依赖关系</p></li>
<li><p>在同一个类中实例化一个类作为该类的实例变量</p></li>
</ul>
<p>尝试增加堆栈大小是个好主意。根据安装的JVM,默认堆栈大小可能会有所不同。</p>
<p><code>-Xss
标志可以用于从项目的配置或命令行增加堆栈的大小。
以上がJava で StackOverflowError エラーの問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undress AI Tool
脱衣画像を無料で

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

testthepdfinanapptodetermineisisiis withthefileoredge.2.enablethebuiltinpdfviewerbyturningoff "Alwaysopenpdffilesexternally" and "downloadpdffiles" inedgesettings.3.Clearbrowsingdataincluding andcachedfilestororeveren

コンテナ化されたJavaアプリケーション:DockerFileを作成し、Eclipse-Temurin:17-jre-Alpineなどの基本的な画像を使用し、JARファイルをコピーしてスタートアップコマンドを定義し、DockerBuildを介して画像を作成し、Dockerrunでローカルに実行します。 2。画像をコンテナレジストリに押します:Dockertagを使用して画像をマークし、DockerHubやその他のレジストリにプッシュします。最初にdockerloginにログインする必要があります。 3. Kubernetesへの展開:展開を書き込み展開を定義し、レプリカの数、コンテナ画像、リソース制限の数を設定し、service.yamlを作成して作成して作成します

importjava.ioandjava.net.socketfori/oandsocketCommunication.2.CreateAsocketObjectToConnectTotheServerusingHostNameandport.3.USEPRINTWRITERTOSENDDATAVIAOUTSTREAMANDBUFFEREDEDEDEDEDEDEREDEREDERTOREADEREADSERVERRESPONSESSTREAM.

VSCODEでは、ショートカットキーを介してパネルと編集領域をすばやく切り替えることができます。左のエクスプローラーパネルにジャンプするには、Ctrl Shift E(Windows/Linux)またはCMD Shift E(MAC)を使用します。編集エリアに戻って、Ctrl `またはescまたはctrl 1〜9を使用します。マウスの操作と比較して、キーボードのショートカットはより効率的であり、エンコードリズムを中断しません。その他のヒントには、ctrl kctrl eフォーカス検索ボックス、f2の名前変更ファイル、ファイルの削除、開いたファイルの入力、矢印キーの展開/崩壊フォルダー。

Javaユニットテストにモッキートを効果的に使用するには、最初にモキト依存関係を追加し、Mavenプロジェクトにモッキートコアの依存関係を追加し、gardleプロジェクトにockito-core:mockito-core:5.7.0 'を追加する必要があります。次に、@mock annotation(@extendwith(mockitoextension.class)と組み合わせて)またはmock()メソッドを介してモックオブジェクトを作成します。次に、(...)。sonreturn(...)およびmockオブジェクトのメソッド動作をスタブするwhen(...)およびその他の方法を使用するか、異なるものを構成できます

JavaSerializationConvertSanobject'sStateIntoAbyTeStreamForStorageorTransmission、およびseRializationは、objectfromthatstream.1.1.toenablesimementtheRializable Interface.2.usobjectputStreamToseRializeAnobject、Savin

awhileloopinjavarepeatedecutesexecuteslongastheconditionistrue;

runthewindowsupdateTroubleshoterviasettings> update&security> troubleShoottoAutoMonissues.2.resetwindowsupDateComponentsは、related -distributionandCatrot2Folders、restartingtherserviceStocleを削除します
