web-dev-qa-db-fra.com

C # intercepte une exception de débordement de pile

J'ai eu un appel récursif à une méthode qui lève une exception de débordement de pile. Le premier appel est entouré d'un bloc catch try, mais l'exception n'est pas interceptée.

L'exception de débordement de pile se comporte-t-elle de manière particulière? Puis-je attraper/gérer correctement l'exception?

NB: si pertinent:

  • l'exception n'est pas jetée dans le fil principal

  • l'objet dans lequel le code lève l'exception est chargé manuellement par Assembly.LoadFrom (...). CreateInstance (...)

106
Toto

À partir de la version 2.0, une exception StackOverflow ne peut être interceptée que dans les cas suivants.

  1. Le CLR est exécuté dans un environnement hébergé* où l'hôte permet spécifiquement de gérer les exceptions StackOverflow
  2. L'exception stackoverflow est levée par code utilisateur et non en raison d'une situation de débordement de pile réelle ( Reference )

*"environnement hébergé" comme dans "mon code héberge le CLR et je configure les options du CLR" et non "mon code s'exécute sur l'hébergement partagé"

101
JaredPar

La bonne façon est de réparer le débordement, mais ...

Vous pouvez vous donner une plus grande pile: -

using System.Threading;
Thread T = new Thread(threadDelegate, stackSizeInBytes);
T.Start();

Vous pouvez utiliser la propriété System.Diagnostics.StackTrace FrameCount pour compter les images que vous avez utilisées et générer votre propre exception lorsqu'une limite d'images est atteinte.

Ou, vous pouvez calculer la taille de la pile restante et lancer votre propre exception lorsqu'elle tombe en dessous d'un seuil: -

class Program
{
    static int n;
    static int topOfStack;
    const int stackSize = 1000000; // Default?

    // The func is 76 bytes, but we need space to unwind the exception.
    const int spaceRequired = 18*1024; 

    unsafe static void Main(string[] args)
    {
        int var;
        topOfStack = (int)&var;

        n=0;
        recurse();
    }

    unsafe static void recurse()
    {
        int remaining;
        remaining = stackSize - (topOfStack - (int)&remaining);
        if (remaining < spaceRequired)
            throw new Exception("Cheese");
        n++;
        recurse();
    }
}

Juste attraper le fromage. ;)

44
user159335

Depuis la page MSDN sur StackOverflowException s:

Dans les versions précédentes du .NET Framework, votre application pouvait intercepter un objet StackOverflowException (par exemple, pour récupérer une récursion sans limite). Toutefois, cette pratique est actuellement déconseillée car un code supplémentaire important est nécessaire pour capturer de manière fiable une exception de dépassement de capacité de la pile et poursuivre l’exécution du programme.

À partir de .NET Framework version 2.0, un objet StackOverflowException ne peut pas être attrapé par un bloc try-catch et le processus correspondant est arrêté par défaut. Par conséquent, les utilisateurs sont invités à écrire leur code pour détecter et prévenir un débordement de pile. Par exemple, si votre application dépend de la récursivité, utilisez un compteur ou une condition d'état pour terminer la boucle récursive. Notez qu'une application qui héberge le CLR (Common Language Runtime) peut spécifier que le CLR décharge le domaine d'application où l'exception de débordement de pile se produit et laisse le processus correspondant se poursuivre. Pour plus d'informations, voir Interface ICLRPolicyManager et Hébergement du Common Language Runtime.

37

Comme plusieurs utilisateurs l'ont déjà dit, vous ne pouvez pas attraper l'exception. Cependant, si vous avez du mal à savoir où cela se passe, vous pouvez configurer Visual Studio pour qu'il s'arrête quand il est lancé.

Pour ce faire, vous devez ouvrir les paramètres d'exception à partir du menu "Débogage". Dans les anciennes versions de Visual Studio, il s'agit de "Débogage" - "Exceptions"; dans les versions plus récentes, c'est dans 'Debug' - 'Windows' - 'Exception Settings'.

Une fois les paramètres ouverts, développez "Exceptions du langage commun", "Système", faites défiler l'écran et cochez la case "System.StackOverflowException". Ensuite, vous pouvez regarder la pile d'appels et rechercher le modèle d'appels répété. Cela devrait vous donner une idée de l'endroit où chercher pour corriger le code à l'origine du débordement de la pile.

21
Simon

Comme mentionné ci-dessus à plusieurs reprises, il n'est pas possible d'intercepter une exception StackOverflowException qui a été générée par le système en raison d'un état de processus corrompu. Mais il existe un moyen de noter l'exception en tant qu'événement:

http://msdn.Microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx

À partir de la version 4 de .NET Framework, cet événement n'est pas déclenché pour les exceptions qui altèrent l'état du processus, telles que les débordements de pile ou les violations d'accès, à moins que le gestionnaire d'événements soit critique pour la sécurité et qu'il possède l'attribut HandleProcessCorruptedStateExceptionsAttribute.

Néanmoins, votre application se terminera après la sortie de la fonction event (une solution de contournement TRÈS sale, qui consistait à redémarrer l'application au sein de cet événement, haha, ne l'a pas fait et ne le fera jamais). Mais c'est suffisant pour vous connecter!

Dans les versions 1.0 et 1.1 du .NET Framework, une exception non gérée qui se produit dans un thread autre que le thread de l'application principale est interceptée par le moteur d'exécution et ne provoque donc pas l'arrêt de l'application. Ainsi, il est possible que l'événement UnhandledException soit déclenché sans que l'application se termine. À partir de la version 2.0 du .NET Framework, ce backstop pour les exceptions non gérées dans les threads enfants a été supprimé, car l’effet cumulatif de ces échecs silencieux incluait une dégradation des performances, des données corrompues et des blocages, qui étaient tous difficiles à déboguer. Pour plus d'informations, y compris une liste des cas dans lesquels le moteur d'exécution ne se termine pas, voir Exceptions dans les threads gérés.

15
FooBarTheLittle

Oui, le dépassement de pile de CLR 2.0 est considéré comme une situation non récupérable. Donc, le runtime a toujours arrêté le processus.

Pour plus de détails, consultez la documentation http://msdn.Microsoft.com/en-us/library/system.stackoverflowexception.aspx

6
Brian Rasmussen

Tu ne peux pas. La CLR ne vous laissera pas faire. Un débordement de pile est une erreur fatale et ne peut pas être récupéré.

5
Matthew Scharley

Vous ne pouvez pas, comme l'expliquent la plupart des posts, laissez-moi ajouter un autre domaine:

Sur de nombreux sites Web, vous verrez des personnes dire que le moyen d'éviter cela consiste à utiliser un AppDomain différent. Si cela se produit, le domaine sera déchargé. Cela est absolument faux (sauf si vous hébergez votre CLR) car le comportement par défaut du CLR déclenchera un événement KillProcess, faisant tomber votre AppDomain par défaut.

5
No hay Problema

C'est impossible, et pour une bonne raison (par exemple, pensez à toutes ces prises (Exception) {} autour).

Si vous souhaitez continuer l'exécution après le dépassement de pile, exécutez un code dangereux dans un autre AppDomain. Les stratégies CLR peuvent être configurées pour mettre fin au débordement actuel d'AppDomain sans affecter le domaine d'origine.

3
ima