Pourquoi est-il possible de récupérer à partir d'une StackOverflowError?

100

Je suis surpris de voir comment il est possible de continuer l'exécution même après une StackOverflowErrorsurvenue en Java.

Je sais que StackOverflowErrorc'est une sous-classe de la classe Error. La classe Error est décrite comme «une sous-classe de Throwable qui indique des problèmes graves qu'une application raisonnable ne devrait pas essayer d'attraper».

Cela ressemble plus à une recommandation qu'à une règle, sous-tendant qu'attraper une erreur comme un StackOverflowError est en fait autorisé et qu'il appartient au programmeur de ne pas le faire. Et voyez, j'ai testé ce code et il se termine normalement.

public class Test
{
    public static void main(String[] args)
    {
        try {
            foo();
        } catch (StackOverflowError e) {
            bar();
        }
        System.out.println("normal termination");
    }

    private static void foo() {
        System.out.println("foo");
        foo();
    }

    private static void bar() {
        System.out.println("bar");
    }
}

Comment se peut-il? Je pense qu'au moment où StackOverflowError est jeté, la pile devrait être si pleine qu'il n'y a pas de place pour appeler une autre fonction. Le bloc de gestion des erreurs s'exécute-t-il dans une pile différente ou que se passe-t-il ici?

user3370796
la source
57
Je fais des erreurs sur StackOverflow tout le temps. Cela ne m'empêche pas de revenir.
10
Yo dawg ... J'ai entendu dire que vous aimiez les débordements de pile, nous avons donc mis un débordement de pile dans votre stackoverflow.com!
Pierre Henry
Parce que les architectures modernes utilisent des pointeurs de trame pour faciliter le déroulement des piles, même partielles. Tant que le code + le contexte pour faire cela ne doivent pas être alloués dynamiquement hors de la pile, il ne devrait pas y avoir de problème.
RBarryYoung

Réponses:

119

Lorsque la pile déborde et StackOverflowErrorest levée, la gestion habituelle des exceptions déroule la pile. Dérouler la pile signifie:

  • abandonner l'exécution de la fonction actuellement active
  • supprimer son frame de pile, continuer avec la fonction appelante
  • abandonner l'exécution de l'appelant
  • supprimer son frame de pile, continuer avec la fonction appelante
  • etc...

... jusqu'à ce que l'exception soit interceptée. Ceci est normal (en fait, nécessaire) et indépendamment de l'exception lancée et pourquoi. Puisque vous attrapez l'exception en dehors du premier appel à foo(), les milliers de foocadres de pile qui remplissaient la pile ont tous été déroulés et la majeure partie de la pile peut être réutilisée.


la source
1
@fge N'hésitez pas à modifier, j'ai envisagé un saut de paragraphe mais je n'ai pas trouvé d'endroit où cela avait l'air bien.
1
Vous pourriez utiliser des puces ... Je suis réticent à modifier le message d'autres personnes;)
fge
1
Le fait est que le plus intérieur s'est footerminé avec un état indéfini, de sorte que tout objet qu'il a pu toucher doit être supposé être brisé. Comme vous ne savez pas dans quelle fonction le débordement de pile s'est produit, seulement qu'il doit être un descendant du trybloc qui l'a attrapé, tout objet pouvant être modifié par n'importe quelle méthode accessible à partir de là est maintenant suspect. Il n'est généralement pas utile de découvrir ce qui s'est passé et d'essayer de le réparer.
Simon Richter
2
@delnan, je pense que la réponse est incomplète sans entrer dans les détails sur les raisons pour lesquelles c'est une mauvaise idée. La différence avec une exception explicitement levée est que Errors ne peut pas être anticipé même lors de l'écriture de code de sécurité d'exception.
Simon Richter
1
@SimonRichter Non, la question est assez précise. Il ne s'agit pas de traiter l' Errorart. L'OP ne demande que sur StackOverflowError, et il demande une chose spécifique sur la gestion de cette erreur: comment un appel de méthode peut-il ne pas échouer lorsque cette erreur est interceptée.
Bakuriu
23

Lorsque le StackOverflowError est levé, la pile est pleine. Cependant, lorsqu'il est intercepté , tous ces fooappels ont été extraits de la pile.barpeut fonctionner normalement car la pile ne déborde plus de foos. (Notez que je ne pense pas que le JLS garantit que vous pouvez récupérer d'un débordement de pile comme celui-ci.)

user2357112 prend en charge Monica
la source
12

Lorsque le StackOverFlow se produit, la JVM apparaîtra vers le crochet, libérant la pile.

Dans votre exemple, il récupère tous les toto empilés.

Nicolas Defranoux
la source
8

Parce que la pile ne déborde pas réellement. Un meilleur nom pourrait être AttemptToOverflowStack. Fondamentalement, cela signifie que la dernière tentative d'ajustement du cadre de la pile est erronée car il n'y a pas assez d'espace libre sur la pile. La pile pourrait en fait avoir beaucoup d'espace, mais pas assez d'espace. Ainsi, quelle que soit l'opération qui aurait dépendu de la réussite de l'appel (généralement une invocation de méthode), elle n'est jamais exécutée et il ne reste plus qu'au programme pour gérer ce fait. Ce qui signifie que ce n'est vraiment pas différent de toute autre exception. En fait, vous pouvez intercepter l'exception dans la fonction qui effectue l'appel.

jmoreno
la source
1
Faites simplement attention si vous faites cela, votre gestionnaire d'exceptions ne nécessite pas plus d'espace de pile que ce qui est disponible!
Vince
2

Comme cela a déjà été répondu , il est possible d'exécuter du code, et en particulier d'appeler des fonctions, après avoir attrapé un StackOverflowErrorcar la procédure normale de gestion des exceptions de la JVM déroule la pile entre throwles catchpoints et, libérant de l'espace de pile à utiliser. Et votre expérience confirme que c'est le cas.

Cependant, ce n'est pas tout à fait la même chose que de dire qu'il est, en général, possible de récupérer d'un fichierStackOverflowError .

Un StackOverflowErrorIS-A VirtualMachineError, qui IS-AN Error. Comme vous le faites remarquer, Java fournit quelques conseils vagues pour Error:

indique des problèmes graves qu'une application raisonnable ne devrait pas essayer d'attraper

et vous, raisonnablement, concluez que cela devrait ressembler à attraper un Errorpourrait être OK dans certaines circonstances. Notez que la réalisation d'une expérience ne démontre pas que quelque chose est, en général, sûr à faire. Seules les règles du langage Java et les spécifications des classes que vous utilisez peuvent le faire. A VirtualMachineErrorest une classe spéciale d'exception, car la spécification du langage Java et la spécification de la machine virtuelle Java fournissent des informations sur la sémantique de cette exception. En particulier, ce dernier dit :

Une implémentation de machine virtuelle Java renvoie un objet qui est une instance d'une sous-classe de la classe VirtualMethodErrorlorsqu'une erreur interne ou une limitation de ressources l'empêche d'implémenter la sémantique décrite dans ce chapitre. Cette spécification ne peut pas prédire où des erreurs internes ou des limitations de ressources peuvent être rencontrées et ne précise pas quand elles peuvent être signalées. Ainsi, l'une des VirtualMethodErrorsous - classes définies ci-dessous peut être lancée à tout moment pendant le fonctionnement de la machine virtuelle Java:

...

  • StackOverflowError: L'implémentation de la machine virtuelle Java n'a plus d'espace de pile pour un thread, généralement parce que le thread effectue un nombre illimité d'appels récursifs suite à une erreur dans le programme en cours d'exécution.

Le problème crucial est que vous «ne pouvez pas prédire» où et quand un StackOverflowErrorsera lancé. Il n'y a aucune garantie sur l'endroit où il ne sera pas lancé. Vous ne pouvez pas vous fier à ce qu'il soit lancé à l' entrée d'une méthode, par exemple. Il peut être jeté à un point dans une méthode.

Cette imprévisibilité est potentiellement désastreuse. Comme elle peut être lancée dans une méthode, elle pourrait être lancée à mi-chemin d'une séquence d'opérations que la classe considère comme une opération "atomique", laissant l'objet dans un état partiellement modifié et incohérent. Avec l'objet dans un état incohérent, toute tentative d'utilisation de cet objet peut entraîner un comportement erroné. Dans tous les cas pratiques, vous ne pouvez pas savoir quel objet est dans un état incohérent, vous devez donc supposer qu'aucun objet n'est digne de confiance. Toute opération de récupération ou tentative de poursuite après que l'exception est interceptée peut donc avoir un comportement erroné. La seule chose sûre à faire est donc de ne pas attraper unStackOverflowError, mais plutôt pour permettre au programme de se terminer. (En pratique, vous pouvez essayer de faire une journalisation des erreurs pour aider au dépannage, mais vous ne pouvez pas compter sur cette journalisation fonctionnant correctement). Autrement dit, vous ne pouvez pas récupérer de manière fiable à partir d'un fichierStackOverflowError .

Raedwald
la source