operator new how delete c++ exception new-operator out-of-memory

how - ¿Puede el operador C++ `new` lanzar una excepción en la vida real?



new keyword c++ (18)

Depende del compilador / tiempo de ejecución y del operator new que esté utilizando (por ejemplo, ciertas versiones de Visual Studio no arrojarán de la caja , sino que preferirían devolver un puntero NULL a la malloc ).

Siempre puede catch una excepción std::bad_alloc , o usar nothrow new para devolver NULL lugar de throwing. (También vea las publicaciones pasadas de StackOverflow que giran alrededor del tema).

Tenga en cuenta que el operator new , como malloc , fallará cuando se haya agotado la memoria, el espacio fuera de la dirección (por ejemplo, 2-3 GB en un proceso de 32 bits según el SO), fuera de cuota (ya se mencionó ulimit ) o fuera de espacio de direcciones contiguas (por ejemplo, montón fragmentado)

¿Puede el new operador lanzar una excepción en la vida real?

Y si es así, ¿tengo alguna opción para manejar una excepción además de matar mi aplicación?

Actualizar:

¿Alguna aplicación real, new pesada comprueba la falla y se recupera cuando no hay memoria?

Ver también:


El nuevo operador y el nuevo operador [] deben lanzar std::bad_alloc , pero este no es siempre el caso ya que el comportamiento puede anularse algunas veces.

Uno puede usar std::set_new_handler y de repente algo completamente diferente puede suceder que tirar std::bad_alloc . Aunque el estándar requiere que el usuario haga que la memoria esté disponible, aborte o lance std::bad_alloc . Pero, por supuesto, este no es el caso.

Descargo de responsabilidad: no estoy sugiriendo hacer esto.


En los sistemas Unix, es habitual ejecutar procesos de larga ejecución con límites de memoria (usando ulimit ) para que no consuma toda la memoria de un sistema. Si su programa alcanza ese límite, obtendrá std::bad_alloc .

Actualización para la edición de OP: el caso más típico de los programas que se recuperan de una condición de falta de memoria se encuentra en sistemas recolectados, que luego realizan un GC y continúan. Sin embargo, este tipo de GC bajo demanda es realmente solo para esfuerzos de última hora; por lo general, los buenos programas intentan GC periódicamente para reducir el estrés en el colector.

Es menos habitual que los programas que no son de GC se recuperen de problemas de falta de memoria, pero para los servidores de Internet, una forma de recuperación es simplemente rechazar la solicitud que está causando que la memoria se agote con un error "temporal". (Estrategia "primero en ser atendido primero").


En muchos casos, no hay una recuperación razonable para una situación de falta de memoria, en cuyo caso es perfectamente razonable dejar que la aplicación finalice. Es posible que desee capturar la excepción en un nivel alto para mostrar un mensaje de error más agradable de lo que el compilador podría dar de forma predeterminada, pero es posible que tenga que jugar algunos trucos para que incluso eso funcione (ya que es probable que el proceso sea muy bajo). recursos en ese punto).

A menos que tenga una situación especial que pueda manejarse y recuperarse, probablemente no haya razón para gastar mucho esfuerzo tratando de manejar la excepción.


Es bueno verificar / detectar esta excepción cuando asigna memoria basada en algo dado desde afuera (desde el espacio de usuario, red, por ejemplo), porque podría significar un intento de comprometer su aplicación / servicio / sistema y no debe permitir que esto ocurra. ocurrir.


La función de controlador nuevo es la función llamada por las funciones de asignación cada vez que falla un nuevo intento de asignar la memoria. Podemos tener nuestro propio registro o alguna acción especial, por ejemplo, organizar más memoria, etc. Su finalidad prevista es una de tres cosas: 1) hacer que haya más memoria disponible 2) terminar el programa (por ejemplo, llamando a std :: terminate) 3 ) lanzar excepción de tipo std :: bad_alloc o derivado de std :: bad_alloc. La implementación predeterminada arroja std :: bad_alloc. El usuario puede tener su propio controlador nuevo, que puede ofrecer un comportamiento diferente al predeterminado. Esto debe usarse solo cuando realmente lo necesites. Vea el ejemplo para más clarificación y comportamiento predeterminado,

#include <iostream> #include <new> void handler() { std::cout << "Memory allocation failed, terminating/n"; std::set_new_handler(nullptr); } int main() { std::set_new_handler(handler); try { while (true) { new int[100000000ul]; } } catch (const std::bad_alloc& e) { std::cout << e.what() << ''/n''; } }


Lo más realista es que lanzará debido a la decisión de limitar un recurso. Digamos que esta clase (que puede consumir mucha memoria) saca la memoria del conjunto de elementos físicos y si muchos objetos la eliminan (necesitamos memoria para otras cosas como sonido, texturas, etc.) puede arrojarse en lugar de colapsar cuando algo que debería ser capaz de asignar memoria lo toma. (parece un extraño efecto secundario).

La sobrecarga nueva puede ser útil en dispositivos con memoria restringida. Como dispositivos de mano o consolas cuando es demasiado fácil ir por la borda con efectos geniales.


No necesita manejar la excepción en cada new :) Las excepciones se pueden propagar. Diseña tu código para que haya ciertos puntos en cada "módulo" donde se maneja ese error.


new arrojará una excepción si no hay más memoria disponible, pero eso no significa que deba envolver cada nuevo en un try ... catch . Solo capte la excepción si su programa realmente puede hacer algo al respecto.

Si el programa no puede hacer nada para manejar esa situación excepcional, lo que sucede a menudo si se queda sin memoria, no sirve de nada captar la excepción. Si lo único que razonablemente puede hacer es abortar el programa, también puede dejar que la excepción suba al nivel superior, donde también terminará el programa.


Sí, new puede lanzar std::bad_alloc (una subclase de std::exception ), que puede atrapar.

Si desea evitar esta excepción y, en su lugar, está listo para probar el resultado de una new para un puntero nulo, puede agregar un argumento de nothrow :

T* p = new (nothrow) T(...); if (p == 0) { // Do something about the bad allocation! } else { // Here you may use p. }


Sí, nuevo puede y arrojará.

Ya que está preguntando por programas ''reales'': he trabajado en varias aplicaciones comerciales de software comercializadas por más de 20 años. Programas ''reales'' con millones de usuarios. Que puedes comprar hoy en el estante. Sí, nuevo puede tirar.

Hay varias formas de manejar esto.

En primer lugar, escriba su propio new_handler (esto se llama antes de que se dé por vencido y se arroje - vea la función set_new_handler ()). Cuando se llama a su nuevo manejador, vea si puede liberar algunas cosas que realmente no necesita. También advierta al usuario que se están quedando sin memoria. (sí, puede ser difícil advertir al usuario sobre cualquier cosa si es realmente bajo).

Una cosa es haber preasignado, al comienzo de tu programa, alguna memoria "extra". Cuando se quede sin memoria, use esta memoria extra para ayudar a guardar una copia del documento del usuario en el disco. Luego advierte, y tal vez salga con gracia.

Etc. Esto es solo una descripción general, obviamente hay mucho más.

Manejar poca memoria no es fácil.


Sí, puede lanzar y lanzará nuevo si falla la asignación. Esto puede suceder si se queda sin memoria o si intenta asignar un bloque de memoria demasiado grande.

Puede capturar la excepción std::bad_alloc y manejarla adecuadamente. A veces esto tiene sentido, otras veces (léase: la mayoría de las veces) no tiene sentido. Si, por ejemplo, intentas asignar un gran buffer pero podría trabajar con menos espacio, podrías intentar asignar bloques sucesivamente más pequeños.


Si está ejecutando un procesador incrustado típico que ejecuta Linux sin memoria virtual, es muy probable que su proceso sea cancelado por el sistema operativo antes de que falle uno nuevo si asigna demasiada memoria.

Si está ejecutando su programa en una máquina con menos memoria física que el máximo de memoria virtual (2 GB en Windows estándar) encontrará que una vez que haya asignado una cantidad de memoria aproximadamente igual a la memoria física disponible, las asignaciones adicionales tendrán éxito pero causará paginación al disco. Esto atascará tu programa y es posible que no puedas llegar al punto de agotar la memoria virtual. Por lo tanto, es posible que no obtenga una excepción.

Si tiene más memoria física que la memoria virtual y simplemente sigue asignando memoria, obtendrá una excepción cuando haya agotado la memoria virtual hasta el punto en que no pueda asignar el tamaño de bloque que está solicitando.

Si tiene un programa de larga ejecución que asigna y libera en muchos tamaños de bloques diferentes, incluidos bloques pequeños, con una gran variedad de vidas, la memoria virtual puede fragmentarse hasta el punto en que los nuevos no podrán encontrar un bloque lo suficientemente grande para satisfacer una solicitud Entonces nuevo lanzará una excepción. Si tiene una fuga de memoria que filtra el pequeño bloque ocasional en una ubicación aleatoria que eventualmente fragmentará la memoria hasta el punto donde fallará una asignación de bloque arbitrariamente pequeña, y se lanzará una excepción.

Si tiene un error de programa que pasa accidentalmente un tamaño de matriz enorme a nuevo [], el nuevo fallará y emitirá una excepción. Esto puede suceder, por ejemplo, si el tamaño de la matriz es en realidad algún tipo de patrón de bytes aleatorio, tal vez derivado de una memoria no inicializada o una secuencia de comunicación dañada.

Todo lo anterior es para el nuevo global predeterminado. Sin embargo, puede reemplazar las nuevas globales y puede proporcionar nuevas específicas para cada clase. Estos también pueden arrojar, y el significado de esa situación depende de cómo lo haya programado. es habitual que los nuevos incluyan un bucle que intente todas las avenidas posibles para obtener la memoria solicitada. Se lanza cuando todos están agotados. Lo que hagas entonces depende de ti.

Puede detectar una excepción de nueva y aprovechar la oportunidad que brinda para documentar el estado del programa en el momento de la excepción. Puedes "descargar núcleo". Si tiene un búfer de instrumentación circular asignado al inicio del programa, puede volcarlo en el disco antes de finalizar el programa. La finalización del programa puede ser elegante, lo que es una ventaja sobre simplemente no manejar la excepción.

No he visto personalmente un ejemplo en el que se pudiera obtener memoria adicional después de la excepción. Una posibilidad sin embargo es la siguiente. Supongamos que tiene un asignador de memoria que es altamente eficiente pero no es bueno para recuperar espacio libre. Por ejemplo, podría ser propenso a la fragmentación del espacio libre, en el que los bloques libres son adyacentes pero no se fusionan. Puede usar una excepción de new, atrapada en new_handler, para ejecutar un procedimiento de compactación de espacio libre antes de volver a intentarlo.

Los programas serios deberían tratar a la memoria como un recurso potencialmente escaso, controlar su asignación lo más posible, controlar su disponibilidad y reaccionar de forma adecuada si algo parece haber sido dramáticamente erróneo. Por ejemplo, podría argumentar que en cualquier programa real hay un límite superior bastante pequeño en el parámetro de tamaño que se pasa al asignador de memoria, y cualquier otro más grande que este debería ocasionar algún tipo de manejo de error, independientemente de si la solicitud puede ser o no satisfecho. Se podría argumentar que la tasa de aumento de la memoria de un programa de larga ejecución debería ser monitoreada, y si se puede predecir razonablemente que el programa agotará la memoria disponible en el futuro cercano, se debe comenzar un reinicio ordenado del proceso.


Tenga en cuenta que en Windows, los nuevos / mallocs muy grandes asignarán desde la memoria virtual. En la práctica, su máquina se bloqueará antes de ver esa excepción.

char *pCrashMyMachine = new char[TWO_GIGABYTES];

¡Pruebalo si te atreves!


Uso Mac OS X, y nunca he visto que malloc devuelva NULL (lo que implicaría una excepción a la new en C ++). La máquina se atasca, hace lo posible para asignar memoria menguada a los procesos, y finalmente envía SIGSTOP e invita al usuario a matar procesos en lugar de tener que lidiar con la falla de asignación.

Sin embargo, esa es solo una plataforma. CIERTAMENTE hay plataformas donde el asignador predeterminado arroja. Y, como dice Chris, ulimit puede introducir una restricción artificial para que una excepción sea el comportamiento esperado.

Además, hay asignadores además del predeterminado / malloc . Si una clase anula el operator new , utiliza argumentos personalizados a new(…) , o pasa un objeto asignador a un contenedor, probablemente defina sus propias condiciones para lanzar bad_alloc .


el nuevo operador lanzará la excepción std :: bad_alloc cuando no haya suficiente memoria disponible en el grupo para cumplir con la solicitud de tiempo de ejecución.

Esto puede suceder en mal diseño o cuando la memoria asignada no se libera correctamente.

El manejo de dicha excepción se basa en su diseño, una forma será hacer una pausa y volver a intentarlo más tarde, con la esperanza de que se devuelva más memoria al grupo y la solicitud pueda tener éxito.


new operador std::bad_alloc excepción std::bad_alloc cuando se quede sin memoria (memoria virtual para ser precisos).

Si new arroja una excepción, entonces es un error grave:

  • Más de VM disponible se está asignando (falla eventualmente). Puede intentar reducir la cantidad de memoria que salir del programa capturando la excepción std::bad_alloc .

osgx dijo:

¿Alguna aplicación del mundo real verifica muchas noticias y puede recuperarse cuando no hay memoria?

He respondido esto previamente en mi respuesta a esta pregunta , que se cita a continuación:

Es muy difícil manejar este tipo de situación. Es posible que desee devolver un error significativo al usuario de la aplicación, pero si se trata de un problema causado por la falta de memoria, es posible que ni siquiera pueda pagar la memoria para asignar el mensaje de error. Es un poco una situación de catch-22 realmente.

Existe una técnica de programación defensiva (a veces llamada paracaídas de memoria o fondo de día lluvioso) en la que asigna un trozo de memoria cuando se inicia la aplicación. Cuando maneje la excepción bad_alloc, liberará esta memoria y usará la memoria disponible para cerrar la aplicación correctamente, lo que incluye mostrar un error significativo al usuario. Esto es mucho mejor que estrellarse :)