2009-12-31 13 views
16

pregunta lo dice todo, pero aquí es un ejemplo:¿Cómo manejar Realloc cuando falla debido a la memoria?

typedef struct mutable_t{ 
    int count, max; 
    void **data; 
} mutable_t; 


void pushMutable(mutable_t *m, void *object) 
{ 
    if(m->count == m->max){ 
     m->max *= 2; 
     m->data = realloc(m->data, m->max * sizeof(void*)); 
    } 
    // how to handle oom?? 
    m->data[m->count++] = object; 
} 

Cómo puedo manipular los quedando sin memoria y no NULL todos mis datos?

editar - supongamos que hay algo que podría hacerse, p. libere algo de memoria en algún lugar o al menos dígale al usuario "no puede hacer eso, se le acabó la memoria". Idealmente, me gustaría dejar lo asignado allí.

+1

altamente dependiente de la aplicación ... pero una cosa es segura, una OOM es bastante crítica. – jldupont

+0

relacionado: http://stackoverflow.com/questions/1941323/always-check-malloced-memory – jldupont

+0

Solo para agregar a un par de respuestas aquí, una idea de cómo manejar un 'realloc()' fallido (en su caso) sería hacer 'm-> max/= 4; m-> max * = 3; 'e intente llamar a' realloc() 'nuevamente para ver si aún podemos exprimir algunos bytes más. Incluso podría probar un par de veces con tamaños sucesivamente más pequeños, pero en algún momento no valdrá la pena. –

Respuesta

18

La técnica estándar es introducir una nueva variable para mantener el retorno de realloc. A continuación, sólo se sobreescribirás la variable de entrada si tiene éxito:

tmp = realloc(orig, newsize); 
if (tmp == NULL) 
{ 
    // could not realloc, but orig still valid 
} 
else 
{ 
    orig = tmp; 
} 
+0

¿Entonces no está establecido en NULL hasta la asignación? Es bueno saber eso. –

+6

¿Y luego qué? No trataste de aumentar el tamaño de tu matriz por diversión, realmente la necesitabas por alguna razón. – Blindy

+2

@Blindy: falla esa operación. Dependiendo de la lógica de la aplicación, le corresponderá decidir cómo recuperarla (quizás este sea un servidor y fallará una solicitud, pero continuará ejecutando otras solicitudes). Pero parece un código de biblioteca de bajo nivel que no debería estar forzando una política de falta de memoria en la aplicación. –

7

Esto es un poco de un tema candente, ya que hay básicamente 2 escuelas de pensamiento sobre el tema

  1. detectar la OOM, y que tiene la función devuelve un código de error.
  2. detectar la OOM y bloquear el proceso lo más rápido posible

Personalmente estoy en el campo # 2. Espere para tipos muy especiales de aplicaciones, OOM es un período fatal. Es cierto que un código perfectamente escrito puede manejar un OOM, pero muy pocas personas entienden cómo escribir código que es seguro ante la falta de memoria. Aún menos se molestan en hacerlo porque casi nunca vale la pena el esfuerzo.

No me gusta pasar el código de error a la función de llamada para OOM porque es el equivalente a decirle a la persona que llama "Fallé y no hay nada que pueda hacer al respecto". En cambio, prefiero estrellarme rápidamente para que el volcado resultante sea lo más instructivo posible.

+1

Se pueden hacer cosas potencialmente sobre la falla de OOM. No hay mucho, pero es posible en algunos casos. (En la mayoría de las aplicaciones, debería haber un envoltorio alrededor de 'malloc()' y 'realloc()' que acaba de salir con un mensaje de error en la falla de memoria, pero no hacen eso para las pocas aplicaciones con mejores soluciones). –

+0

@Chris, ciertamente cierto y algunos productos (por ejemplo, el servidor SQL) son bastante buenos. Sin embargo, esos productos son la rara excepción. Hacer las cosas bien requiere una cantidad asombrosa de disciplina, cumplimiento y comprensión. Tanto es así que las personas rara vez intentan hacerlo bien. – JaredPar

+0

@JaredPar, por lo que básicamente dices porque la mayoría de las personas no manejan correctamente los errores, ni siquiera debes preocuparte por los errores y dejar que la aplicación se bloquee y se queme, posiblemente corrompiendo los datos del usuario. El problema es que OOM ocurre en tiempo de ejecución en la máquina del usuario. No tiene control sobre los tamaños de memoria en estas máquinas y en el espacio de HD para el archivo de intercambio. Luego agregue pérdidas de memoria ... Además, es bastante fácil probar que su aplicación pueda manejarlo. Utilice un contenedor para malloc/realloc que devuelve aleatoriamente NULL. – Secure

2

¡Ese es su problema! Aquí hay algunos criterios:

  • Usted pidió esa memoria por alguna razón. Si no está disponible, ¿está condenado el trabajo de su programa o puede seguir haciendo algo? Si el primero, desea terminar su programa con un mensaje de error; de lo contrario, puede mostrar un mensaje de error de alguna manera y continuar.

  • ¿Existe la posibilidad de intercambiar tiempo por espacio? ¿Podría responder cualquier operación que haya intentado con un algoritmo que utiliza menos memoria? Eso suena como un montón de trabajo, pero en realidad sería una posibilidad para continuar el funcionamiento de su programa a pesar de no tener suficiente memoria inicialmente.

  • ¿Sería incorrecto que su programa siguiera cojeando sin estos datos y sin memoria suficiente? Si es así, debe terminar con un mensaje de error. Es mucho mejor matar tu programa que continuar procesando datos incorrectos.

3

La primera regla que sigue shoud cuando se trabaja con realloc no es para asignar el valor de retorno de realloc al mismo puntero que le pasan. Este

m->data = realloc(m->data, m->max * sizeof(void*)); 

es malo. Si realloc falla, devuelve un puntero nulo, pero no desasigna la memoria anterior.El código anterior anulará su m->data, mientras que el antiguo bloque de memoria apuntado anteriormente por m->data muy probablemente se convertirá en pérdida de memoria (si no tiene otras referencias).

El valor de retorno de realloc se debe almacenar en un puntero separado primero

void **new_data; 
... 
new_data = realloc(m->data, m->max * sizeof(void*)); 

A continuación, puede comprobar el éxito/fracaso y cambiar el valor de m->data en caso de éxito

if (new_data != NULL) 
    m->data = new_data; 
else 
    /* whatever */; 
1

Hay también otro error sutil que puede venir de realloc. La pérdida de memoria procedente del puntero NULL devuelto es bastante conocida (pero bastante raro de encontrar). Tuve en mi programa un bloqueo de vez en cuando que provenía de una llamada realloc. Tenía una estructura dinámica que ajusta su tamaño automáticamente con un realloc se asemeja a éste:

m->data = realloc(m->data, m->max * sizeof(void*)); 

El error que hice fue no verificar m-> == 0 max, que liberó el área de memoria. Y hecho desde mi puntero m-> data a rancio.

Sé que es un poco fuera de tema, pero este fue el único problema real que he tenido con realloc.

+0

Lo divertido que acabo de descubrir ahora (es decir, en 2016) es que el stdlib que utilicé en ese momento no siguió correctamente el estándar, ya que 'realloc()' es necesario para devolver 'NULL' en el caso de una llamada con 0 longitud. Esto no habría desencadenado el error en primer lugar. Fascinante, porque recuerdo muy bien ese error, que ocurrió alrededor de 2004 en una máquina Solaris muy antigua (ya por esa época). –

2
  1. Descubre cómo el marco de aplicación maneja un OOM. Muchos simplemente no manejarán un OOM. La mayoría de las veces un framework no funcionará correctamente en condiciones de RAM no libre a menos que diga muy claramente y sin ambigüedades en algún lugar que lo haga. Si el framework no manejará un OOM y es multiproceso (muchos lo son hoy en día), un OOM será el final del show para el proceso en muchos casos. Incluso si no es multiproceso, aún puede estar cerca del colapso. Ya sea que salga del proceso o el marco sí puede ser un punto discutible; una salida inmediata predecible puede ser un poco mejor que una caída en algún punto semi-aleatorio en el futuro cercano.

  2. Si está utilizando una agrupación de submemoria de propósito especial separada (es decir, no su malloc habitual) para un conjunto bien definido de operaciones que solo están limitadas en la memoria utilizada por OOM (es decir, la operación actual está rodada volver o abortar limpiamente en OOM para el grupo de sub-memoria, no todo el proceso o grupo de memoria principal), y ese sub-pool tampoco es usado por el framework de la aplicación, o si su framework y TODO del resto de la aplicación está diseñado para mantener un estado significativo y una operación continua en condiciones de RAM no libre (raro pero no inaudito en el modo kernel y algunos tipos de programación de sistemas), puede estar en lo cierto al devolver un código de error en lugar de bloquear el proceso.

  3. Lo ideal sería que la mayor parte de las asignaciones de memoria (o incluso más idealmente todos las asignaciones) para una pieza de procesamiento deben asignarse tan pronto como sea posible en el proceso, a ser posible antes de que comience adecuadamente, para reducir al mínimo los problemas de los datos pérdida de integridad y/o cantidad de codificación de retrotracción requerida si falla. En la práctica, gran parte del tiempo para ahorrar costos y tiempo de programación en proyectos preservar las aplicaciones de integridad de datos depende de las transacciones de la base de datos y requiere que el usuario/persona de soporte detecte un bloqueo de la GUI (o bloqueo del servidor) y reinicie la aplicación cuando no esté los errores de memoria ocurren, en lugar de escribirse para hacer frente y revertir en todas y cada una de las miles de posibles situaciones OOM de la mejor manera posible. Luego, los esfuerzos se centran en tratar de limitar la exposición de la aplicación a situaciones de sobrecarga, que pueden incluir validación adicional y límites en el tamaño de los datos y conexiones y consultas simultáneas.

  4. Incluso si comprueba la cantidad de memoria disponible, a menudo otro código puede asignar o liberar memoria como lo hace, cambiando la base de la comprobación de memoria y posiblemente llevando a OOM. Por lo tanto, la comprobación de la RAM libre disponible antes de la asignación no suele ser una solución confiable al problema de garantizar que su aplicación opera dentro de los límites de RAM disponibles y mantiene la integridad de los datos lo suficiente para satisfacer a los usuarios.

  5. La mejor situación es saber cuánta memoria necesita su aplicación en todos los casos posibles, incluidos los gastos generales de infraestructura, y mantener esa cifra dentro de la cantidad de RAM disponible para su aplicación, pero los sistemas a menudo son tan complicado con dependencias externas que dictan el tamaño de los datos, por lo que lograrlo puede ser poco realista.

La prueba de fuego, por supuesto, se están satisfaciendo las que los usuarios lo suficientemente alta a través de los tiempos, y los datos poco frecuente la corrupción, la pérdida o se bloquea. En algunos casos, es útil una aplicación que tenga un proceso de monitor para reiniciarlo si falla.

Por lo que respecta realloc:

comprobar el valor de retorno de realloc - ponerlo en una variable temporal. Solo importa si es NULL si el nuevo tamaño solicitado fue> 0. En otros casos colocarlo en su variable no temporal:

por ejemplo

void* temp = realloc(m->data, m->max * sizeof(void*)); 
    if (m->max!=0&&temp==NULL) { /* crash or return error */ } 
    m->data =(void**)temp; 

EDITAR

Cambiado "la mayoría de los casos" a "una gran cantidad de casos" en (1).

Reconozco que ha dicho que "se puede hacer algo" si la memoria no se puede asignar. Pero la administración de la memoria es una consideración muy global (!).

8

La estrategia sobre qué hacer cuando falla realloc() depende de su aplicación. La pregunta es demasiado genérica para ser respondida en todos los casos posibles.

Algunas otras notas:

Nunca hacen:

a = realloc(a, size); 

Si realloc() falla, se pierde el puntero original y realloc() no free() la memoria original, por lo que obtendrá una pérdida de memoria. En su lugar, hacer:

tmp = realloc(a, size); 
if (tmp) 
    a = tmp; 
else 
    /* handle error */ 

segundo punto que quiero hacer es menor y puede que no sea tan crítico, pero es bueno saber acerca de todos modos: el aumento de la memoria para ser asignado por un factor f es bueno. Digamos que malloc()n bytes primero. Entonces necesita más memoria, por lo que realloc() con el tamaño n × f. Entonces necesita más memoria, por lo que necesita n × f bytes. Si quieres realloc() utilizar el espacio de los dos bloques de memoria anteriores, usted quiere asegurarse de que × n f ≤ n + n × f.Resolviendo esta ecuación, obtenemos f ≤ (sqrt (5) +1)/2 = 1.618 (el Golden ratio). Utilizo un factor de 1.5 la mayoría de las veces.

+0

¿tiene más material sobre algoritmos de asignación de memoria? – Jori

1

He encontrado el problema. La configuración es OS: win7 (64); IDE: vs2013; Debug (Win32).
Cuando mi realloc devolvió nulo debido a la memoria. Tengo dos soluciones:

1. Cambie la propiedad del proyecto para habilitar DIRECCIONES GRANDES.
2. Cambie mi plataforma de solución de Win32 a x64.

Cuestiones relacionadas