334 Stimmen

Versuchen-Schließlich-Block verhindert StackOverflowError

Schauen Sie sich die folgenden beiden Methoden an:

public static void foo() {
    try {
        foo();
    } finally {
        foo();
    }
}

public static void bar() {
    bar();
}

Das Ausführen von bar() führt offensichtlich zu einem StackOverflowError, aber das Ausführen von foo() nicht (das Programm scheint einfach endlos zu laufen). Warum ist das so?

0 Stimmen

Erlaubt die JVM keine Optimierung von Endaufrufen? Das würde die endlose Rekursion in eine endlose Schleife umwandeln.

17 Stimmen

Formell wird das Programm letztendlich stoppen, da Fehler, die während der Verarbeitung der finally Klausel auftreten, an die nächste höhere Ebene weitergeleitet werden. Aber halten Sie nicht den Atem an; die Anzahl der Schritte beträgt etwa 2 zur (maximalen Stapeltiefe) und das Werfen von Ausnahmen ist auch nicht gerade billig.

0 Stimmen

@dan04 Während es dies ermöglichen könnte, was lässt dich glauben, dass es sich um eine korrekte Optimierung handelt? (Hinweis: Das ist es nicht, da das Ende des finally Blocks nicht frei ist.)

1voto

Vitruvie Punkte 2307

Das Programm scheint nur für immer zu laufen; es endet tatsächlich, benötigt jedoch exponentiell mehr Zeit, je mehr Stapelspeicher vorhanden ist. Um zu zeigen, dass es endet, habe ich ein Programm geschrieben, das zuerst den Großteil des verfügbaren Stapelspeichers verbraucht, dann foo aufruft und schließlich eine Spur dessen schreibt, was passiert ist:

foo 1
  foo 2
    foo 3
    Finally 3
  Finally 2
    foo 3
    Finally 3
Finally 1
  foo 2
    foo 3
    Finally 3
  Finally 2
    foo 3
    Finally 3
Exception in thread "main" java.lang.StackOverflowError
    at Main.foo(Main.java:39)
    at Main.foo(Main.java:45)
    at Main.foo(Main.java:45)
    at Main.foo(Main.java:45)
    at Main.consumeAlmostAllStack(Main.java:26)
    at Main.consumeAlmostAllStack(Main.java:21)
    at Main.consumeAlmostAllStack(Main.java:21)
    ...

Der Code:

import java.util.Arrays;
import java.util.Collections;
public class Main {
  static int[] orderOfOperations = new int[2048];
  static int operationsCount = 0;
  static StackOverflowError fooKiller;
  static Error wontReachHere = new Error("Won't reach here");
  static RuntimeException done = new RuntimeException();
  public static void main(String[] args) {
    try {
      consumeAlmostAllStack();
    } catch (RuntimeException e) {
      if (e != done) throw wontReachHere;
      printResults();
      throw fooKiller;
    }
    throw wontReachHere;
  }
  public static int consumeAlmostAllStack() {
    try {
      int stackDepthRemaining = consumeAlmostAllStack();
      if (stackDepthRemaining < 9) {
        return stackDepthRemaining + 1;
      } else {
        try {
          foo(1);
          throw wontReachHere;
        } catch (StackOverflowError e) {
          fooKiller = e;
          throw done; //nicht genügend Stapelspeicher, um eine neue Ausnahme zu erzeugen
        }
      }
    } catch (StackOverflowError e) {
      return 0;
    }
  }
  public static void foo(int depth) {
    //System.out.println("foo " + depth); Nicht genügend Stapelspeicher, um dies zu tun...
    orderOfOperations[operationsCount++] = depth;
    try {
      foo(depth + 1);
    } finally {
      //System.out.println("Finally " + depth);
      orderOfOperations[operationsCount++] = -depth;
      foo(depth + 1);
    }
    throw wontReachHere;
  }
  public static String indent(int depth) {
    return String.join("", Collections.nCopies(depth, "  "));
  }
  public static void printResults() {
    Arrays.stream(orderOfOperations, 0, operationsCount).forEach(depth -> {
      if (depth > 0) {
        System.out.println(indent(depth - 1) + "foo " + depth);
      } else {
        System.out.println(indent(-depth - 1) + "Finally " + -depth);
      }
    });
  }
}

Sie können es online ausprobieren! (Einige Durchläufe könnten foo öfter oder seltener aufrufen)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X