82 votes

Quand je "jette" quelque chose, où est-il stocké en mémoire ?

Je comprends que lorsque quelque chose est throw n, la pile est "déroulée" jusqu'au point où elle est capturée, et les destructeurs des instances de classe sur la pile dans chaque contexte de fonction sont exécutés (c'est pourquoi vous ne devriez pas lancer une exception à partir d'un destructeur - vous pourriez finir par en lancer une deuxième)... mais je me demande où en mémoire l'objet que j'ai lancé est stocké pendant que cela se produit ?

Cela dépend-il de l'implémentation ? Si oui, existe-t-il une méthode particulière utilisée par les compilateurs les plus populaires ?

51voto

NPE Points 169956

Oui, la réponse dépend du compilateur.

Une expérience rapide avec mon compilateur ( g++ 4.4.3 ) révèle que sa bibliothèque d'exécution essaie d'abord de malloc pour l'exception et, en cas d'échec, tente d'allouer de l'espace dans un "tampon d'urgence" à l'échelle du processus qui vit sur le segment de données. Si cela ne fonctionne pas, il appelle std::terminate() .

Il semblerait que l'objectif principal du tampon d'urgence soit de pouvoir lancer std::bad_alloc après que le processus ait épuisé l'espace du tas (dans ce cas, l'option malloc échouerait).

La fonction concernée est la suivante __cxa_allocate_exception :

extern "C" void *
__cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) throw()
{
  void *ret;

  thrown_size += sizeof (__cxa_refcounted_exception);
  ret = malloc (thrown_size);

  if (! ret)
    {
      __gnu_cxx::__scoped_lock sentry(emergency_mutex);

      bitmask_type used = emergency_used;
      unsigned int which = 0;

      if (thrown_size > EMERGENCY_OBJ_SIZE)
        goto failed;
      while (used & 1)
        {
          used >>= 1;
          if (++which >= EMERGENCY_OBJ_COUNT)
            goto failed;
        }

      emergency_used |= (bitmask_type)1 << which;
      ret = &emergency_buffer[which][0];

    failed:;

      if (!ret)
        std::terminate ();
    }

  // We have an uncaught exception as soon as we allocate memory.  This
  // yields uncaught_exception() true during the copy-constructor that
  // initializes the exception object.  See Issue 475.
  __cxa_eh_globals *globals = __cxa_get_globals ();
  globals->uncaughtExceptions += 1;

  memset (ret, 0, sizeof (__cxa_refcounted_exception));

  return (void *)((char *)ret + sizeof (__cxa_refcounted_exception));
}

Je ne sais pas si ce schéma est typique.

20voto

De cette page :

Le stockage est nécessaire pour les exceptions qui sont lancées. Ce stockage doit persister pendant que la pile est déroulée, puisqu'elle sera utilisé par le gestionnaire, et doit être être à l'abri des fils. Le stockage d'objets d'exception sera donc normalement alloué dans le tas bien que Les implémentations peuvent fournir un tampon d'urgence pour supporter le lancement d'exceptions exceptions bad_alloc dans des conditions de faible mémoire.

Maintenant, il ne s'agit que de l'ABI de l'Itanium et je cherche des détails spécifiques à GCC, Clang et MSVC. Cependant, la norme ne précise rien et cela semble être la manière évidente d'implémenter le stockage des exceptions, donc...

4voto

Kiril Kirov Points 19081

Je ne sais pas si ça va répondre à votre question, mais ceci (Comment un compilateur C++ implémente la gestion des exceptions) est un excellent article sur la gestion des exceptions à tous : . Je le recommande vivement ( :

Désolé pour la réponse courte, mais l'ensemble des informations contenues dans l'article est excellent, je ne peux pas choisir et poster quelques informations ici.

0voto

Caleb Points 72897

La norme C++ spécifie généralement le comportement du langage, mais pas la manière dont le compilateur doit mettre en œuvre ce comportement. Je pense que cette question entre dans cette catégorie. La meilleure façon d'implémenter quelque chose comme ça dépend des spécificités de la machine -- certains processeurs ont beaucoup de registres d'usage général, d'autres en ont très peu. Un processeur pourrait même être construit avec un registre spécial pour les exceptions, auquel cas le compilateur devrait être libre de tirer parti de cette fonctionnalité.

-2voto

James Kanze Points 96599

Eh bien, il ne peut pas être sur la pile, car il va être déroulé, et il ne peut pas être sur le tas, car cela signifierait que le système ne pourrait probablement pas lancer std::bad_alloc . En dehors de cela, c'est complètement à la charge de l'implémentation : pas une implémentation spécifiée (qui doit être documentée), mais non spécifiée. (Une implémentation pourrait utiliser le tas la plupart du temps, tant qu'elle a une sorte de sauvegarde d'urgence qui permettra un nombre limité d'exceptions même lorsqu'il n'y a plus de mémoire).

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