Saltar al contenido

¿Cómo lidiar con bad_alloc en C ++?

Este grupo de especialistas luego de varios días de investigación y recopilar de información, dieron con la solución, nuestro deseo es que te sea útil para tu plan.

Solución:

En general tu no poder, y no debería intentarlo, para responder a este error.bad_alloc indica que no se puede asignar un recurso porque no hay suficiente memoria disponible. En la mayoría de los escenarios, su programa no puede esperar hacer frente a eso, y terminar pronto es el único comportamiento significativo.

Peor aún, los sistemas operativos modernos a menudo sobreasignan: en tales sistemas, malloc y new puede devolver un puntero válido incluso si no queda suficiente memoria libre – std::bad_alloc nunca será arrojado, o al menos no es un signo confiable de agotamiento de la memoria. En cambio, intenta acceso la memoria asignada resultará en una falla de segmentación, que no es detectable (puede resolver la señal de fallo de segmentación, pero no se puede reanudar el programa posteriormente).

Lo único que podías hacer al atrapar std::bad_alloc es quizás registrar el error e intentar garantizar una terminación segura del programa liberando recursos pendientes (pero esto se hace automáticamente en el curso normal de desenrollado de la pila después de que se arroja el error si el programa usa RAII adecuadamente).

En ciertos casos, el programa puede intentar liberar algo de memoria y volver a intentarlo, o usar memoria secundaria (= disco) en lugar de RAM, pero estas oportunidades solo existen en escenarios muy específicos con condiciones estrictas:

  1. La aplicación debe asegurarse de que se ejecuta en un sistema que no comprometa demasiado la memoria, es decir, indica un fallo en la asignación en lugar de hacerlo más tarde.
  2. La aplicación debe poder liberar memoria inmediatamente, sin más asignaciones accidentales mientras tanto.

Es extremadamente raro que las aplicaciones tengan control sobre el punto 1: las aplicaciones del espacio de usuario. Nunca hacer, es una configuración de todo el sistema que requiere permisos de root para cambiar.1

Bien, supongamos que ha fijado el punto 1. Lo que ahora puede hacer es, por ejemplo, utilizar una caché LRU para algunos de sus datos (probablemente algunos objetos comerciales particularmente grandes que se pueden regenerar o recargar a pedido). A continuación, debe poner la lógica real que puede fallar en una función que admita el reintento; en otras palabras, si se cancela, puede simplemente reiniciarla:

lru_cache widget_cache;

double perform_operation(int widget_id) 
    std::optional maybe_widget = widget_cache.find_by_id(widget_id);
    if (not maybe_widget) 
        maybe_widget = widget_cache.store(widget_id, load_widget_from_disk(widget_id));
    
    return maybe_widget->frobnicate();


…

for (int num_attempts = 0; num_attempts < MAX_NUM_ATTEMPTS; ++num_attempts) 
    try 
        return perform_operation(widget_id);
     catch (std::bad_alloc const&) 
        if (widget_cache.empty()) throw; // memory error elsewhere.
        widget_cache.remove_oldest();
    


// Handle too many failed attempts here.

Pero incluso aquí, usando std::set_new_handler en lugar de manejar std::bad_alloc proporciona el mismo beneficio y sería mucho más sencillo.


1 Si está creando una aplicación que lo hace punto de control 1, y estás leyendo esta respuesta, por favor envíame un correo electrónico. Tengo mucha curiosidad por conocer tus circunstancias.

¿Cuál es el comportamiento especificado por el estándar C ++ de new en c ++?

La noción habitual es que si new El operador no puede asignar memoria dinámica del tamaño solicitado, entonces debería lanzar una excepción de tipo std::bad_alloc.
Sin embargo, algo más sucede incluso antes de que bad_alloc se lanza una excepción:

C ++ 03 Sección 3.7.4.1.3: dice

Una función de asignación que no puede asignar almacenamiento puede invocar el new_handler (18.4.2.2) instalado actualmente, si lo hubiera. [Note: A program-supplied allocation function can obtain the address of the currently installed new_handler using the set_new_handler function (18.4.2.3).] Si una función de asignación declarada con una especificación de excepción vacía (15.4), throw (), no puede asignar almacenamiento, devolverá un null puntero. Cualquier otra función de asignación que no asigne almacenamiento solo indicará una falla lanzando una excepción de la clase std :: bad_alloc (18.4.2.1) o una clase derivada de std :: bad_alloc.

Considere el siguiente ejemplo de código:

#include 
#include 

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()

    std::cerr << "Unable to satisfy request for memoryn";

    std::abort();


int main()

    //set the new_handler
    std::set_new_handler(outOfMemHandler);

    //Request huge memory size, that will cause ::operator new to fail
    int *pBigDataArray = new int[100000000L];

    return 0;

En el ejemplo anterior, operator new (lo más probable) no podrá asignar espacio para 100,000,000 enteros, y la función outOfMemHandler() se llamará y el programa se cancelará después de emitir un mensaje de error.

Como se ve aquí, el comportamiento predeterminado de new operador cuando no puede cumplir con una solicitud de memoria, es llamar al new-handler funcionar repetidamente hasta que encuentre suficiente memoria o no haya más controladores nuevos. En el ejemplo anterior, a menos que llamemos std::abort(), outOfMemHandler() sería llamado repetidamente. Por lo tanto, el manejador debe asegurarse de que la siguiente asignación sea exitosa, o registrar otro manejador, o no registrar ningún manejador, o no regresar (es decir, terminar el programa). Si no hay un nuevo controlador y la asignación falla, el operador lanzará una excepción.

Cuál es el new_handler y set_new_handler?

new_handler es un typedef para un puntero a una función que no toma ni devuelve nada, y set_new_handler es una función que toma y devuelve un new_handler.

Algo como:

typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();

El parámetro set_new_handler es un puntero al operador de función new debería llamar si no puede asignar la memoria solicitada. Su valor de retorno es un puntero a la función de controlador previamente registrada, o null si no hubo un manejador anterior.

¿Cómo manejar las condiciones de falta de memoria en C ++?

Dado el comportamiento de newUn programa de usuario bien diseñado debe manejar las condiciones de falta de memoria proporcionando un new_handlerque hace una de las siguientes:

Haga más memoria disponible: Esto puede permitir que el próximo intento de asignación de memoria dentro del bucle del operador new tenga éxito. Una forma de implementar esto es asignar un gran bloque de memoria al inicio del programa y luego liberarlo para usarlo en el programa la primera vez que se invoca el nuevo controlador.

Instale un nuevo controlador diferente: Si el nuevo manejador actual no puede hacer más memoria disponible, y hay otro nuevo manejador que puede, entonces el nuevo manejador actual puede instalar el otro nuevo manejador en su lugar (llamando set_new_handler). La próxima vez que el operador llame a la función de nuevo controlador, obtendrá la que se instaló más recientemente.

(Una variación de este tema es que un nuevo controlador modifique su propio comportamiento, por lo que la próxima vez que se invoca, hace algo diferente. Una forma de lograr esto es hacer que el controlador nuevo modifique static, datos globales o específicos del espacio de nombres que afectan el comportamiento del nuevo controlador).

Desinstale el nuevo controlador: Esto se hace pasando un null puntero a set_new_handler. Sin un nuevo controlador instalado, operator new arrojará una excepción ((convertible a) std::bad_alloc) cuando la asignación de memoria no se realiza correctamente.

Lanzar una excepción convertible a std::bad_alloc. Tales excepciones no son capturadas por operator new, pero se propagará al sitio que origina la solicitud de memoria.

No volver: Llamando abort o exit.

Puede detectarlo como cualquier otra excepción:

try 
  foo();

catch (const std::bad_alloc&) 
  return -1;

Todo lo que pueda hacer útilmente desde este punto depende de usted, pero definitivamente es factible técnicamente.

valoraciones y comentarios

Si te ha sido de ayuda este post, nos gustaría que lo compartas con el resto seniors de este modo contrubuyes a difundir este contenido.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *