82 votos

Cuando `throw` algo, dónde se almacena en la memoria?

Yo entiendo que cuando algo es thrown, la pila está 'desprendido' hasta el punto donde es capturado, y los destructores de instancias de la clase en la pila en cada función del contexto de ejecución (que es por qué usted no debe lanzar una excepción de un destructor - usted podría terminar encima de lanzar un segundo)...pero me pregunto donde en la memoria el objeto de que he tirado es almacenado mientras esto sucede?

Es que depende de la implementación? Si es así, ¿hay un método particular utilizado por la mayoría de los compiladores populares?

51voto

NPE Puntos 169956

Sí, la respuesta es compilador-dependiente.

Un experimento rápido con mi compilador (g++ 4.4.3), revela que su biblioteca de tiempo de ejecución primer lugar, intenta malloc memoria de la excepción, y, en su defecto, los intentos de asignar espacio dentro de un proceso de "emergencia buffer" que vive en el segmento de datos. Si eso no funciona, llama a std::terminate().

Parecería que el principal propósito de la emergencia de búfer es ser capaz de tirar std::bad_alloc después de que el proceso se ha agotado el espacio de montón (en cuyo caso el malloc de llamada de fallar).

La función relevante es __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));
}

No sé cómo típico de este esquema es.

20voto

Desde esta página:

El almacenamiento es necesario para excepciones de tirado. Este almacenamiento se debe conservar mientras que la pila está en fase de desenrollado, ya que será utilizada por el controlador, y debe ser seguro para subprocesos. Objeto de excepción almacenamiento será, por tanto, normalmente se asignados en el montón, aunque las implementaciones pueden proporcionar un emergencia de búfer para apoyar a tirar bad_alloc excepciones en virtud de memoria baja condiciones.

Ahora, esto es sólo el Itanium ABI y estoy en busca de detalles específicos de GCC, Clang y MSVC. Sin embargo, el estándar no especifica nada y esta parece ser la forma más obvia de aplicar la excepción de almacenamiento, así que...

4voto

Kiril Kirov Puntos 19081

No sé si esto va a responder a su pregunta, pero esto (Cómo compilador de C implementa la gestión de excepciones) es excelente artículo sobre el manejo de excepciones en absoluto:. Lo recomiendo encarecidamente (:

Lo siento por la respuesta corta, pero toda la información en el artículo es grande, no puedo recoger y publicar alguna información aquí.

0voto

Caleb Puntos 72897

El estándar de C generalmente especifica cómo el lenguaje se comporta, pero no cómo el compilador debe aplicar ese comportamiento. Creo que esta cuestión entra en esa categoría. La mejor manera de implementar algo como esto depende de las características específicas de la máquina - algunos procesadores tienen un montón de registros de propósito general, algunas tienen muy pocos. Un procesador incluso podría ser construido con un registro especial para las excepciones, en cuyo caso el compilador debe ser libre de tomar ventaja de esta característica.

-2voto

James Kanze Puntos 96599

Bien, no puede ser en la pila, ya que va a ser desenrollado, y no puede ser en el montón, ya que significaría que el sistema probablemente no podía tirar std::bad_alloc. Aparte de eso, es totalmente de la aplicación: no es de aplicación especifica (que debe ser documentado), pero sin especificar. (Una aplicación podría utilizar el montón de la mayoría de las veces, como siempre que tiene algún tipo de emergencia que permitirá un número limitado de excepciones, incluso cuando no hay más memoria).

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X