2009-03-27 9 views
9

Estoy escribiendo algunas pautas para la empresa y necesito responder algunas preguntas difíciles. Este es bastante difícil.Todavía atrapa las fallas de asignación de memoria en su programa C++

La solución puede ser:

  1. no registra en absoluto. Asegúrese de que los objetos se asignan utilizando new que arrojará una excepción cuando falle la asignación. La aplicación morirá, y no es un gran problema. El código PRO generalmente puede ser muy limpio.

  2. Realice un seguimiento de los errores de asignación de memoria y notifíquelo en consecuencia, como cualquier error (como error de acceso a archivos).

Honestamente, tengo que escribir código mucho más si vamos con la opción 2. Por ejemplo, muchos std :: Tring operaciones implican la asignación de memoria. Tal como

std :: string str1, str2; str1 = str2; str + = str2;

Nuestro software siempre ejecutará plataformas principales, no integradas. De alguna manera, creo que la opción 1 es el camino a seguir. ¿Cual es tu opinion?

Respuesta

5

A menos que esté haciendo algunas asignaciones bastante impresionantes, es poco probable que golpee una falla de asignación en un espacio de memoria virtual de 32 bits. (Y aún menos probable en un sistema de 64 bits). Probablemente sea mejor morir si te quedas sin memoria. En el caso raro de que algo salga mal y se quede sin memoria, es poco probable que pueda informar un error de todos modos. (A menos que, por supuesto, reserve específicamente una reserva de memoria para liberar en caso de una falla de asignación.)

Una posibilidad: asignar una gran cantidad de memoria para uso de emergencia solamente, luego capturar excepciones de memoria en un nivel bastante alto en su aplicación, libere la memoria de emergencia, registre lo que sucedió y luego muera.

+0

Me gusta esa respuesta, especialmente el segundo párrafo. –

9

En general, no verifique las fallas de asignación de memoria en asignaciones pequeñas. Inevitablemente, es más problemático de lo que vale, y de todos modos es difícil hacerlo bien. Y la mayoría de las veces no hay nada que puedas hacer al respecto. En operaciones de memoria muy grandes, si puede hacer algo al respecto, podría valer la pena considerar las cosas caso por caso.

Esto está bien cubierto por C++ Gotchas: Avoiding Common Problems in Coding and Design. En particular, ver Gotcha #61: Checking for Allocation Failure:

Algunas preguntas debería no ser preguntó, y si una determinada asignación de memoria ha tenido éxito es uno de ellos.

[...] código de comprobación de errores que es este involucrados rara vez es del todo correcto al principio y casi nunca es correcta después de un período de mantenimiento. Un mejor enfoque no es comprobar en todo :

String **array = new String *[n]; 
for(String **p = array; p < array+n; ++p) 
    *p = new String; 

Este código es más corto, más claro, más rápido, y correcta.El comportamiento estándar de nuevo es lanzar una excepción bad_alloc en caso de error de asignación. Esto nos permite encapsular el código de error para la asignación falla del resto del programa, resultando en un diseño más limpio, más claro y generalmente más eficiente.

+0

Consulte http://www.ddj.com/cpp/184401393 para obtener un mejor artículo sobre este tema. –

0

nada malo en simplemente morir cuando falla la asignación. muy probablemente, todo lo demás después de eso fallaría de todos modos.

simplemente verifique que realmente se obtiene una excepción por no asignar, y no más tarde porque se usa un puntero NULL.

+0

En C++, new arroja una excepción excepto cuando se llama con (nothrow). Haga una búsqueda global de "nothrow", "malloc", "realloc" y "calloc", y debería estar listo para continuar. –

1

Si detecta la excepción de falta de memoria, puede obtener el seguimiento de la pila donde ocurrió la falla, que el 99% del tiempo es todo lo que necesita para diagnosticar el problema. Ese es entonces tu registro.

Puede bloquear un buffer de 1MB o algo que puede usar para generar esta información, ya sea usándola directamente o liberándola para que la memoria esté disponible mientras se crea el registro.

13

Atrapo las asignaciones de memoria, pero solo ocasionalmente.

En particular, yo de vez en cuando trampa de una asignación de memoria donde:

  • sé la cantidad de memoria asignada es muy grande
  • Hay algo que pueda hacer al respecto si falla la asignación (es decir, : manejar con gracia la condición con un aviso al usuario, etc.)

Dicho esto, esas dos cosas son bastante raras, por lo general acabo dejando que el programa muera de la excepción.

2

Sí, lo hago, pero depende de su aplicación. Para aplicaciones de misión crítica, muchas aplicaciones de servidor y servicios que se ejecutan en un sistema cliente, no debe bloquearse en casos OOM: las condiciones de memoria insuficiente pueden surgir temporalmente y el usuario esperaría que su código siguiera funcionando después de que el problema desapareciera. .

Imagínese si un cerdo de memoria comienza a ejecutarse en su sistema, y ​​de repente su shell, su navegador web, aplicaciones, etc., fallan debido a ello. Esa no sería una buena experiencia para el usuario.

Por otro lado, si la suya es una herramienta única donde OOM significa que no puede lograr lo único que el usuario le está pidiendo, la falla probablemente sea correcta.

Independientemente, incluso para el caso no controlado, debe agregar un nivel máximo de captura que puede hacer un poco de registro en el caso de que el OOM sea realmente causado por su código.

+2

En realidad, su "mala experiencia de usuario" es una realidad para todas las distribuciones modernas de Linux. Si la memoria del núcleo se llena, el "Asesino sin memoria" o "Asesino OOM" comenzará a matar procesos. El núcleo hace esto, y no hay absolutamente nada que puedas hacer al respecto. – Tom

+0

Un caso particular de que OOM Killer sea problemático es ejecutar una máquina multinúcleo de memoria máxima en un sistema operativo de 32 bits. El kernel solo obtiene 1GB de memoria, y las estructuras de datos necesarias para gestionar 15GB de RAM pueden llenarlo fácilmente. ¿El resultado? ¡Los procesos de vim/screen/bash acabaron con la izquierda y la derecha! – Tom

+0

Además, en Linux, al menos, cualquier excepción no detectada provocará un abort() con un volcado del núcleo. Esto se puede utilizar para identificar la condición OOM, y es muy superior al registro debido a la capacidad de realizar un análisis de programa post-mortem completo desde el volcado del núcleo. – Tom

1

En un sistema operativo moderno, su computadora se congelará y probablemente se caiga por mucho tiempo, mucho antes de que realmente se quede sin VM. Es una prueba sin sentido para eso.

+0

@leeor_net ¡Excelente contraejemplo! ¿Por qué no hacer una respuesta? –

+0

Además, al menos algunos sistemas operativos modernos (algunas versiones de Linux) sobrepasan la memoria y tienen la misma probabilidad de matar tu proceso que informar al programa del problema a través de los canales habituales, es decir, bad_alloc para nuevo y un puntero nulo para malloc. Otra razón más por la cual este tipo de controles son completamente inútiles en la era de la memoria virtual. (Inserte advertencias habituales sobre las plataformas integradas aquí.) – user168715

+0

"este tipo de comprobaciones son completamente inútiles": las simplificaciones excesivas rara vez son ciertas y este es uno de estos casos. Si intenta asignar una gran región continua a la vez, puede obtener un bad_alloc incluso en una máquina Linux moderna. Ocurre con mi código ahora mismo. – ypnos

1

En el caso del 99.9%, todas mis aplicaciones C++ simplemente morirán en una asignación fallida. Una vez que te quedas sin memoria, realmente no hay nada que puedas hacer a menos que tu aplicación esté específicamente diseñada para manejar y corregir las condiciones de falta de memoria.

El.El 1% de casos es para casos donde se realiza una asignación que es 1) que se sabe que es muy grande y tiene una posibilidad significativa de falla y 2) representa una situación donde es apropiado un repliegue apropiado. Esto es muy raro y han pasado años desde que intenté algo así (no volvería a hacerlo).

0

No verificaría en el sitio de llamadas, pero recomendaría incluir la mayor parte del programa en una cláusula catch para registrar y, con suerte, ejecutar errores (el error de asignación de memoria podría indicar una pérdida de memoria, un bucle fugitivo mientras gestión de memoria (asignación de múltiples memorias intermedias pero solo utilizando uno a la vez), etc.).

+0

Es mejor dejar que el programa descargue el núcleo del std :: bad_alloc no capturado, de modo que pueda inspeccionar el estado completo del programa una vez que haya finalizado. Es menos "bonito", pero mucho más útil para arreglar el error. – Tom

+0

Esa es una solución viable, también. –

+0

bad_alloc throws no siempre indica un error. Como ejemplo, estoy desarrollando un editor de mapas. A veces, con grandes dimensiones, se lanzará bad_alloc. Esto no indica un error, solo que las dimensiones de los mapas son demasiado grandes para caber en la memoria de una máquina de usuarios. Este no es el caso donde quiero que el programa se bloquee y es muy recuperable usando un simple bloque try/catch al crear un mapa con el operador 'nuevo'. Si falla, simple, desalojo cualquier resto de memoria y continúo alegremente. Concedido no todos los casos de bad_alloc son así de sencillos. –

6

Según otra sugerencia, estoy haciendo de esto una respuesta real.

Muchas sugerencias hasta ahora básicamente han declarado que "bad_alloc == error en su programa". Por el contrario, bad_alloc throws no siempre indica un error.

Como ejemplo, estoy desarrollando un editor de mapas para un proyecto de juego en el que estoy trabajando. A veces, con grandes dimensiones de mapa, se lanzará bad_alloc. Esto no indica un error, solo que las dimensiones de los mapas son demasiado grandes para caber en la memoria disponible en una máquina de usuarios. Este no es el caso donde quiero que el programa se bloquee y es muy recuperable usando un simple bloque try/catch al crear un mapa con el operador 'nuevo'. Si falla, simple, desalojo cualquier resto de memoria y continúo alegremente. De acuerdo, no todos los casos de bad_alloc son tan sencillos, así que es importante comprender realmente por qué se lanza un bad_alloc.

Cuestiones relacionadas