Ich habe diese Frage gestellt, um zu erfahren, wie die Größe des Laufzeitaufrufstapels in der JVM erhöht werden kann. Ich habe eine Antwort darauf und ich habe auch viele nützliche Antworten und Kommentare, die relevant dafür sind, wie Java mit der Situation umgeht, in der ein großer Laufzeitstapel benötigt wird. Ich habe meine Frage mit der Zusammenfassung der Antworten erweitert.
Ursprünglich wollte ich die Größe des JVM-Stacks erhöhen, damit Programme wie a ohne laufen StackOverflowError
.
public class TT {
public static long fact(int n) {
return n < 2 ? 1 : n * fact(n - 1);
}
public static void main(String[] args) {
System.out.println(fact(1 << 15));
}
}
Die entsprechende Konfigurationseinstellung ist das java -Xss...
Befehlszeilenflag mit einem ausreichend großen Wert. Für das TT
obige Programm funktioniert es mit OpenJDKs JVM folgendermaßen:
$ javac TT.java
$ java -Xss4m TT
Eine der Antworten hat auch darauf hingewiesen, dass die -X...
Flags implementierungsabhängig sind. Ich habe benutzt
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1~8.04.3)
OpenJDK 64-Bit Server VM (build 16.0-b13, mixed mode)
Es ist auch möglich, einen großen Stapel nur für einen Thread anzugeben (siehe in einer der Antworten, wie). Dies wird empfohlen, um java -Xss...
zu vermeiden, dass Speicher für Threads verschwendet wird, die ihn nicht benötigen.
Ich war neugierig, wie groß ein Stapel ist, den das obige Programm genau benötigt, also habe ich ihn n
vergrößert ausgeführt:
- -Xss4m kann ausreichen für
fact(1 << 15)
- -Xss5m kann ausreichen für
fact(1 << 17)
- -Xss7m kann ausreichen für
fact(1 << 18)
- -Xss9m kann ausreichen für
fact(1 << 19)
- -Xss18m kann ausreichen für
fact(1 << 20)
- -Xss35m kann ausreichen für
fact(1 << 21)
- -Xss68m kann ausreichen für
fact(1 << 22)
- -Xss129m kann ausreichen für
fact(1 << 23)
- -Xss258m kann ausreichen für
fact(1 << 24)
- -Xss515m kann ausreichen für
fact(1 << 25)
Aus den obigen Zahlen geht hervor, dass Java für die obige Funktion ungefähr 16 Bytes pro Stapelrahmen verwendet, was vernünftig ist.
Die obige Aufzählung kann genug sein, anstatt genug zu sein , da die Stapelanforderung nicht deterministisch ist: Sie wird mehrmals mit derselben Quelldatei ausgeführt, und dieselbe ist -Xss...
manchmal erfolgreich und ergibt manchmal eine StackOverflowError
. ZB für 1 << 20, -Xss18m
war genug in 7 von 10 Läufen und -Xss19m
war auch nicht immer genug, aber -Xss20m
war genug (in allen 100 Läufen von 100). Verursacht die Speicherbereinigung, das Einschalten der JIT oder etwas anderes dieses nicht deterministische Verhalten?
Die Stapelverfolgung, die bei a StackOverflowError
(und möglicherweise auch bei anderen Ausnahmen) gedruckt wird, zeigt nur die neuesten 1024 Elemente des Laufzeitstapels. Eine Antwort unten zeigt, wie die genaue erreichte Tiefe gezählt wird (die viel größer als 1024 sein kann).
Viele Leute, die geantwortet haben, haben darauf hingewiesen, dass es eine gute und sichere Codierungspraxis ist, alternative, weniger stapelhungrige Implementierungen desselben Algorithmus in Betracht zu ziehen. Im Allgemeinen ist es möglich, in eine Reihe von rekursiven Funktionen in iterative Funktionen umzuwandeln (unter Verwendung eines z. B. Stack
Objekts, das auf dem Heap anstatt auf dem Laufzeitstapel aufgefüllt wird). Für diese spezielle fact
Funktion ist es ziemlich einfach, sie zu konvertieren. Meine iterative Version würde folgendermaßen aussehen:
public class TTIterative {
public static long fact(int n) {
if (n < 2) return 1;
if (n > 65) return 0; // Enough powers of 2 in the product to make it (long)0.
long f = 2;
for (int i = 3; i <= n; ++i) {
f *= i;
}
return f;
}
public static void main(String[] args) {
System.out.println(fact(1 << 15));
}
}
Zu Ihrer Information, wie die obige iterative Lösung zeigt, kann die fact
Funktion die exakte Fakultät von Zahlen über 65 (sogar über 20) nicht berechnen, da der in Java integrierte Typ long
überlaufen würde. Ein Refactoring fact
, bei dem ein BigInteger
statt zurückgegeben wird, long
würde auch bei großen Eingaben genaue Ergebnisse liefern.