Pourquoi est-il possible de récupérer une erreur StackOverflowError?

Je suis surpris de voir comment il est possible de continuer l’exécution même après qu’une StackOverflowError soit survenue en Java.

Je sais que StackOverflowError est un sous-ensemble 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 doit pas tenter de détecter”.

Cela ressemble plus à une recommandation qu’à une règle, car l’atsortingbution d’une erreur comme une StackOverflowError est en fait autorisée et il incombe au programmeur de ne pas le faire. Et voir, j’ai testé ce code et il se termine normalement.

 public class Test { public static void main(Ssortingng[] 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 lancé, la stack doit être tellement pleine qu’il n’y a plus de place pour appeler une autre fonction. Le bloc de gestion des erreurs est-il en cours d’exécution dans une stack différente, ou que se passe-t-il ici?

Lorsque la stack déborde et que StackOverflowError est lancé, la gestion des exceptions habituelles déroule la stack. Dérouler la stack signifie:

  • interrompre l’exécution de la fonction actuellement active
  • supprimer son frame de stack, continuer avec la fonction d’appel
  • interrompre l’exécution de l’appelant
  • supprimer son frame de stack, continuer avec la fonction d’appel
  • etc…

… jusqu’à ce que l’exception soit interceptée. Ceci est normal (en fait, nécessaire) et indépendant de l’exception levée et pourquoi. Puisque vous attrapez l’exception en dehors du premier appel à foo() , les milliers de trames de stack totales qui ont rempli la stack ont toutes été déroulées et la majeure partie de la stack est libre d’être réutilisée.

Lorsque StackOverflowError est lancé, la stack est pleine. Cependant, quand il est attrapé , tous ces appels ont été sortis de la stack. bar peut fonctionner normalement parce que la stack ne déborde plus de foo s. (Notez que je ne pense pas que le JLS garantit que vous pouvez récupérer d’un tel débordement de stack.)

Lorsque le StackOverFlow se produit, la machine virtuelle Java apparaîtra au verrou, libérant la stack.

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

Parce que la stack 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 stack est erronée car il ne rest plus assez d’espace libre sur la stack. La stack pourrait en fait avoir beaucoup d’espace, juste 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 ne sera jamais appliquée et il ne restra plus au programme qu’à 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.

Comme il a déjà été répondu , il est possible d’exécuter du code, et en particulier d’appeler des fonctions, après avoir capturé un StackOverflowError car la procédure normale de gestion des exceptions de la JVM déroule la stack entre le throw et les points de StackOverflowError . 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 généralement possible de récupérer à partir d’une StackOverflowError .

Un StackOverflowError IS-A VirtualMachineError , quelle Error IS-AN. Comme vous le soulignez, Java fournit de vagues conseils pour une Error :

indique des problèmes graves qu’une application raisonnable ne devrait pas tenter de saisir

et vous pouvez raisonnablement conclure que cela peut sembler attraper une Error peut être OK dans certaines circonstances. Notez que réaliser 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. Un VirtualMachineError est une classe spéciale d’exception, car la spécification de langage Java et la spécification de machine virtuelle Java fournissent des informations sur la sémantique de cette exception. En particulier, ce dernier dit :

Une implémentation Java Virtual Machine lève un object qui est une instance d’une sous-classe de la classe VirtualMethodError lorsqu’une erreur interne ou une limitation de ressource l’empêche d’implémenter la sémantique décrite dans ce chapitre. Cette spécification ne permet pas de prédire où des erreurs internes ou des limitations de ressources peuvent être rencontrées et ne précise pas à quel moment elles peuvent être rapscopes. Ainsi, toutes les sous-classes VirtualMethodError définies ci-dessous peuvent être lancées à 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 stack pour un thread, généralement parce que le thread effectue un nombre illimité d’invocations récursives suite à une défaillance du programme en cours d’exécution.

Le problème crucial est que vous “ne pouvez pas prédire” où ou quand un StackOverflowError sera lancé. Il n’y a aucune garantie quant à l’endroit où il ne sera pas lancé. Par exemple, vous ne pouvez pas vous attendre à ce qu’il soit lancé à l’ entrée d’une méthode. Il pourrait être lancé à un point dans une méthode.

Cette imprévisibilité est potentiellement désastreuse. Comme elle peut être lancée dans une méthode, elle peut être renvoyée en partie à travers une séquence d’opérations que la classe considère comme une opération “atomique”, laissant l’object dans un état partiellement modifié, incohérent. Avec l’object dans un état incohérent, toute tentative d’utilisation de cet object pourrait entraîner un comportement erroné. Dans tous les cas pratiques, vous ne pouvez pas savoir quel object est dans un état incohérent, vous devez donc supposer qu’aucun object n’est fiable. Toute opération de récupération ou tentative de poursuite après que l’exception ait été interceptée pourrait donc avoir un comportement erroné. La seule chose sûre à faire est donc de ne pas intercepter une StackOverflowError , mais plutôt d’autoriser la fin du programme. (Dans la pratique, vous pouvez tenter d’effectuer une journalisation des erreurs pour faciliter le dépannage, mais vous ne pouvez pas vous fier à cette journalisation pour fonctionner correctement). En d’autres StackOverflowError , vous ne pouvez pas récupérer de manière fiable une StackOverflowError .