So lösen Sie das StackOverflowError-Fehlerproblem in Java
Einführung in StackOverflowError
StackOverflowError kann für Java-Entwickler ärgerlich sein, da es einer der häufigsten Laufzeitfehler ist, auf die wir stoßen können. In diesem Artikel erfahren Sie anhand verschiedener Codebeispiele, wie dieser Fehler auftritt und wie Sie damit umgehen. Wie Stack Frames und StackOverflowerError auftreten Beginnen wir mit den Grundlagen. Wenn eine Methode aufgerufen wird, wird ein neuer Stapelrahmen auf dem Aufrufstapel erstellt. Der Stack-Frame enthält die Parameter der aufgerufenen Methode und ihrer lokalen Variablen.
StackOverflowError kann für Java-Entwickler ärgerlich sein, da es einer der häufigsten Laufzeitfehler ist, auf die wir stoßen können.
In diesem Artikel werden wir anhand verschiedener Codebeispiele verstehen, wie dieser Fehler auftritt und wie man damit umgeht.
Wie Stack Frames und StackOverflowerError auftreten
Beginnen wir mit den Grundlagen. Wenn eine Methode aufgerufen wird, wird ein neuer Stapelrahmen auf dem Aufrufstapel erstellt. Dieser Stapelrahmen enthält die Parameter der aufgerufenen Methode, ihre lokalen Variablen und die Rücksprungadresse der Methode. Dies ist der Punkt, an dem die Methodenausführung nach der Rückkehr der aufgerufenen Methode fortgesetzt werden soll.
Die Erstellung von Stapelrahmen wird fortgesetzt, bis das Ende des Methodenaufrufs innerhalb der verschachtelten Methode erreicht ist.
Wenn die JVM während dieses Vorgangs auf eine Situation stößt, in der kein Platz zum Erstellen eines neuen Stapelrahmens vorhanden ist, wird ein StackOverflower
-Fehler ausgegeben. StackOverflower
错误。
JVM遇到这种情况的最常见原因是未终止/无限递归——StackOverflowerr的Javadoc描述提到,错误是由于特定代码段中的递归太深而引发的。
然而,递归并不是导致此错误的唯一原因。在应用程序不断从方法内调用方法直到堆栈耗尽的情况下,也可能发生这种情况。这是一种罕见的情况,因为没有开发人员会故意遵循糟糕的编码实践。另一个罕见的原因是方法中有大量局部变量。
当应用程序设计为类之间具有循环关系时,也可以抛出StackOverflowError。在这种情况下,会重复调用彼此的构造函数,从而引发此错误。这也可以被视为递归的一种形式。
另一个引起此错误的有趣场景是,如果一个类在同一个类中作为该类的实例变量实例化。这将导致一次又一次(递归)调用同一类的构造函数,最终导致堆栈溢出错误。
StackOverflowerError正在运行
在下面所示的示例中,由于意外递归,开发人员忘记为递归行为指定终止条件,将抛出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
当遇到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)🎜Hier gilt für jeden an die Methode übergebenen Wert , In jedem Fall wird ein Fehler ausgegeben: 🎜rrreee🎜 Im nächsten Beispiel wird jedoch die Abbruchbedingung angegeben, wenn jedoch der Wert
-1
an calculateFacttorial()</ übergeben wird code> Methode, dann wird die Beendigungsbedingung nie erfüllt, was zu einer nicht abschließenden/unendlichen Rekursion führt: 🎜rrreee🎜 Diese Testreihe demonstriert dieses Szenario: 🎜rrreee🎜In diesem speziellen Fall, wenn die Beendigungsbedingung einfach ausgedrückt wird als: 🎜rrreee 🎜 Der folgende Test zeigt diese Situation in der Praxis: 🎜rrreee🎜 Schauen wir uns nun ein Szenario an, in dem ein StackOverflowError-Fehler aufgrund einer zirkulären Beziehung zwischen Klassen auftritt. Betrachten wir <code>ClassOne
und ClassTwo
, die sich gegenseitig in ihren Konstruktoren instanziieren und so eine zirkuläre Beziehung erzeugen: 🎜rrreeerrreee🎜 Nehmen wir nun an, dass wir versuchen, ClassOne zu instanziieren, wie gezeigt in diesem Test: 🎜rrreee🎜Dies führt letztendlich zu einem StackOverflowError, weil der Konstruktor von ClassOne
ClassTwo
instanziiert, während der Konstruktor von ClassTwo
ClassOne
noch einmal. Dies geschieht wiederholt, bis der Stapel überläuft. 🎜🎜Als nächstes schauen wir uns an, was passiert, wenn eine Klasse in derselben Klasse wie eine Instanzvariable dieser Klasse instanziiert wird. 🎜🎜Wie im nächsten Beispiel gezeigt, instanziiert sich AccountHolder
selbst als Instanzvariable JointaCountHolder
: 🎜rrreee🎜Wenn die Klasse AccountHolder
instanziiert wird, da Rekursive Aufrufe des Konstruktors, die einen StackOverflowError auslösen, wie in diesem Test gezeigt: 🎜rrreee🎜StackOverflowError auflösen🎜🎜Wenn ein StackOverflowError auftritt, besteht die beste Vorgehensweise darin, den Stack-Trace sorgfältig zu untersuchen, um Duplikate des Zeilennummernmodells zu identifizieren. Dadurch können wir Code mit problematischer Rekursion lokalisieren. 🎜🎜Lassen Sie uns einige Stapelspuren untersuchen, die durch das Codebeispiel verursacht werden, das wir zuvor gesehen haben. 🎜🎜Wenn die erwartete Ausnahmedeklaration ignoriert wird, wird dieser Stack-Trace von InfiniteCursionWithTerminationConditionManualTest
generiert: 🎜rrreee🎜Hier können Sie sehen, dass Zeile 5 wiederholt wird. Hier werden die rekursiven Aufrufe durchgeführt. Jetzt müssen Sie nur noch den Code überprüfen, um festzustellen, ob die Rekursion korrekt erfolgt. 🎜🎜Hier ist der Stack-Trace, den wir durch die Ausführung von CyclicDependancyManualTest
erhalten (wiederum wird keine Ausnahme erwartet): 🎜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
标志可以用于从项目的配置或命令行增加堆栈的大小。
Das obige ist der detaillierte Inhalt vonSo lösen Sie das StackOverflowError-Fehlerproblem in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undress AI Tool
Ausziehbilder kostenlos

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Der Schlüssel zum Schreiben von PHP -Kommentaren liegt in der Klärung des Zwecks und der Spezifikationen. Kommentare sollten "Warum" und nicht "was getan" erklären, um Redundanz oder zu Einfachheit zu vermeiden. 1. Verwenden Sie ein einheitliches Format wie Docblock (/*/) für Klassen- und Methodenbeschreibungen, um die Lesbarkeit und die Kompatibilität der Werkzeuge zu verbessern. 2. Betonen Sie die Gründe für die Logik, z. B. warum JS -Sprünge manuell ausgeben müssen. 3. Fügen Sie eine Übersichtsbeschreibung vor komplexem Code hinzu, beschreiben Sie den Prozess in Schritten und helfen Sie, die Gesamtidee zu verstehen. V. Gute Anmerkungen können die Kommunikationskosten senken und die Effizienz der Code -Wartung verbessern.

Der Schlüssel zum Schreiben guter Kommentare besteht darin, "warum" zu erklären, anstatt nur "was getan wurde", um die Lesbarkeit des Codes zu verbessern. 1. Kommentare sollten logische Gründe erklären, wie z. B. Überlegungen für die Wertauswahl oder -verarbeitung; 2. Verwenden Sie Absatzanmerkungen für eine komplexe Logik, um die Gesamtidee von Funktionen oder Algorithmen zusammenzufassen. Fegen regelmäßig Kommentare beibehalten, um die Konsistenz mit dem Code zu gewährleisten, irreführend und gegebenenfalls veraltete Inhalte zu löschen. V.

Kommentare können nicht nachlässig sein, da sie die Gründe für die Existenz des Codes und nicht die Funktionen erklären möchten, z. B. die Kompatibilität mit alten Schnittstellen oder Einschränkungen von Drittanbietern, sonst können Personen, die den Code lesen, nur auf Vermutungen angewiesen. Die Bereiche, die kommentiert werden müssen, umfassen komplexe bedingte Urteile, spezielle Fehlerbehandlungslogik und vorübergehende Bypass -Beschränkungen. Eine praktischere Möglichkeit, Kommentare zu schreiben, besteht darin, Einzelzeilen-Kommentare auszuwählen oder Kommentare basierend auf der Szene zu blockieren. Verwenden Sie Dokumentblock Kommentare, um Parameter zu erläutern und Werte zu Beginn von Funktionen, Klassen und Dateien zurückzugeben, und halten Sie die Kommentare aktualisiert. Für eine komplexe Logik können Sie dem vorherigen eine Zeile hinzufügen, um die Gesamtabsicht zusammenzufassen. Verwenden Sie gleichzeitig keine Kommentare zum Versiegelungscode, sondern verwenden Sie Versionsteuerungswerkzeuge.

Der erste Schritt besteht darin, das integrierte Umgebungspaket XAMPP oder MAMP auszuwählen, um einen lokalen Server zu erstellen. Der zweite Schritt besteht darin, die entsprechende PHP -Version entsprechend den Projektanforderungen auszuwählen und mehrere Versionen zu konfigurieren. Der dritte Schritt besteht darin, VSCODE oder PHPSTORM als Editor auszuwählen und mit Xdebug zu debuggen. Darüber hinaus müssen Sie Komponist, PHP_CODESNIFFER, PHPUNIT und andere Tools installieren, um die Entwicklung zu unterstützen.

Es gibt drei allgemeine Möglichkeiten, PHP-Kommentare zu verwenden: Einzeilen-Kommentare sind geeignet, um die Codelogik kurz zu erklären, z. B. // oder # für die Erläuterung der aktuellen Zeile. Multi-Line-Kommentare /*...*/ eignen sich für eine detaillierte Beschreibung der Funktionen oder Klassen; Dokumentieren Sie Kommentare DocBlock beginnen mit /**, um Eingabeaufforderungsinformationen für die IDE bereitzustellen. Wenn Sie es verwenden, sollten Sie Unsinn vermeiden, weiterhin synchron aktualisieren und keine Kommentare verwenden, um Codes für lange Zeit zu blockieren.

Der Schlüssel zum Schreiben von PHP -Kommentaren ist klar, nützlich und prägnant. 1. Kommentare sollten die Absicht hinter dem Code erläutern, anstatt nur den Code selbst zu beschreiben, z. B. den logischen Zweck komplexe bedingte Urteile zu erklären. 2. Fügen Sie Kommentare zu Schlüsselszenarien wie magische Werte, alte Codekompatibilität, API -Schnittstellen usw. hinzu, um die Lesbarkeit zu verbessern. 3. Vermeiden Sie doppelte Codeinhalte, halten Sie ihn präzise und spezifisch und verwenden Sie Standardformate wie PHPDOC. 4. Die Kommentare sollten synchron mit dem Code aktualisiert werden, um die Genauigkeit zu gewährleisten. Aus der Sicht anderer sollten gute Kommentare angesehen werden, die Kosten des Verständnisses senken und zu einem Codes verstehen, das Navigationsgerät versteht.

PHP -Vergleichsbetreiber müssen auf die Typ -Conversion -Probleme aufmerksam machen. 1. Verwendung == nur zu vergleichen, um Werte zu vergleichen, und die Typumwandlung wird durchgeführt, wie z. B. 1 == "1" ist wahr; 2. Verwendung ===, um den gleichen Wert zu benötigen wie der Typ, wie z. B. 1 === "1" ist falsch; 3. Größenvergleich kann für Werte und Zeichenfolgen wie "Apple" verwendet werden

Der Schlüssel zum Schreiben von PHP -Kommentaren besteht darin, "Warum" und nicht "was zu tun" zu erklären, den Annotationsstil des Teams zu vereinen, doppelte Code -Kommentare zu vermeiden und Todo und Fixme -Tags vernünftigerweise zu verwenden. 1. Kommentare sollten sich darauf konzentrieren, die logischen Gründe für den Code zu erklären, wie z. B. Leistungsoptimierung, Algorithmusauswahl usw.; 2. Das Team muss die Annotationsspezifikationen vereinen, z. 3. Vermeiden Sie bedeutungslose Anmerkungen, die nur den Inhalt des Codes nacherdigen und die Geschäftsbedeutung ergänzen sollten. V.
