329 votes

essayez-bloc finally empêche StackOverflowError

Jetez un oeil à l'une des deux méthodes suivantes:

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

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

L'exécution bar() clairement les résultats en StackOverflowError, mais l'exécution foo() ne le fait pas (le programme semble juste de s'exécuter indéfiniment). Pourquoi est-ce?

329voto

Peter Lawrey Points 229686

Il n'est pas toujours courir. Chaque débordement de pile provoque le code pour atteindre le bloc finally. Le problème est qu'il faut vraiment, vraiment longtemps. L'ordre du temps est O(2^N) où N est la valeur maximale de la pile de la profondeur.

Imaginez la profondeur maximale est de 5

foo() calls
    foo() calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
    finally calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
finally calls
    foo() calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
    finally calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()

Chaque niveau dans le bloc finally prendre deux fois plus longtemps qu'une pile de profondeur pourrait être De 10 000 ou plus. Si vous pouvez faire 10 000 000 d'appels par seconde, cela va prendre 10^3003 secondes ou plus longtemps que l'âge de l'univers.

40voto

ninjalj Points 22026

Lorsque vous obtenez une exception de l'appel d' foo() à l'intérieur de l' try, vous appelez foo() de finally et de commencer à recursing de nouveau. Quand qui provoque une autre exception, vous vous appelez foo() à partir d'un autre intérieure finally(), et ainsi de suite presque à l'infini.

38voto

Alex Coleman Points 3927

Essayez d'exécuter le code suivant:

    try {
        throw new Exception("TEST!");
    } finally {
        System.out.println("Finally");
    }

Vous trouverez que le bloc finally est exécuté avant de lancer une Exception jusqu'à un niveau au dessus. (Sortie:

Enfin

Exception in thread "main" java.lang.Exception: le TEST! au test.main(test.java:6)

Cela est logique, car, enfin, est appelée juste avant la sortie de la méthode. Cela signifie, cependant, qu'une fois que vous obtenez ce premier StackOverflowError, il va essayer de le jeter, mais enfin, doivent exécuter en premier, de sorte qu'il s'exécute foo() encore, qui devient un autre dépassement de la pile, et en tant que tel fonctionne enfin à nouveau. Ça arrive tout le temps à jamais, de sorte que l'exception n'est jamais imprimé.

Dans votre barre de méthode cependant, dès que l'exception se produit, il est tout simplement jetés à la verticale jusqu'au niveau au-dessus, et sera imprimé

26voto

WhozCraig Points 32734

Dans l'effort à fournir une preuve raisonnable que cette terminera finalement, je vous offre plutôt vide de sens du code. Remarque: Java n'est PAS ma langue, par un tronçon de la plus vive imagination. Je proférer ce uniquement à l'appui de la réponse de Pierre, qui est la réponse correcte à la question.

Cette tente de simuler les conditions de ce qui arrive quand une invoquer ne peut PAS arriver, car elle permettrait de créer un débordement de pile. Il me semble la chose la plus difficile des gens ne parviennent pas à saisir dans l'invoquer n'arrive pas quand il ne peut pas se produire.

public class Main
{
    public static void main(String[] args)
    {
        try
        {   // invoke foo() with a simulated call depth
            Main.foo(1,5);
        }
        catch(Exception ex)
        {
            System.out.println(ex.toString());
        }
    }

    public static void foo(int n, int limit) throws Exception
    {
        try
        {   // simulate a depth limited call stack
            System.out.println(n + " - Try");
            if (n < limit)
                foo(n+1,limit);
            else
                throw new Exception("StackOverflow@try("+n+")");
        }
        finally
        {
            System.out.println(n + " - Finally");
            if (n < limit)
                foo(n+1,limit);
            else
                throw new Exception("StackOverflow@finally("+n+")");
        }
    }
}

La sortie de cette peu inutile tas de goo est la suivante, et à l'exception interceptée peut venir comme une surprise; Oh, et 32 essayez-les appels (2^5), qui est entièrement attendus:

1 - Try
2 - Try
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
2 - Finally
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
1 - Finally
2 - Try
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
2 - Finally
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
java.lang.Exception: StackOverflow@finally(5)

23voto

Karoly Horvath Points 45145

Apprenez à tracer votre programme:

public static void foo(int x) {
    System.out.println("foo " + x);
    try {
        foo(x+1);
    } 
    finally {
        System.out.println("Finally " + x);
        foo(x+1);
    }
}

C'est la sortie que je vois:

[...]
foo 3439
foo 3440
foo 3441
foo 3442
foo 3443
foo 3444
Finally 3443
foo 3444
Finally 3442
foo 3443
foo 3444
Finally 3443
foo 3444
Finally 3441
foo 3442
foo 3443
foo 3444
[...]

Comme vous pouvez le voir le StackOverFlow est jeté à certaines couches ci-dessus, de sorte que vous pouvez faire des récursivité étapes jusqu'à ce que vous frappez une autre exception, et ainsi de suite. C'est une infinie "boucle".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X