2010-07-24 15 views
8

Probablemente todo el mundo se topó con este problema al menos una vez durante el desarrollo:¿Cómo evitar el "intercambio de la muerte" durante el desarrollo?

while(/*some condition here that somehow never will be false*/) 
{ 
    ... 
    yourvector.push_back(new SomeType()); 
    ... 
} 

Como se ve el programa comienza a drenar toda la memoria del sistema, se bloquea su programa y su sistema comienza a cambiar a lo loco. Si no reconoce el problema lo suficientemente rápido y mata el proceso, probablemente obtenga un sistema que no responde en segundos, donde el puntero del mouse ni siquiera se mueve. Puede esperar a que se bloquee el programa con un error de "falta de memoria" (que puede demorar varios minutos) o al restablecer en su computadora.

Si no puede localizar el error inmediatamente a continuación, tendrá varias pruebas y restablece para averiguar que es muy molesto ...

Busco ser un medio multiplataforma para evitar que esto de algun modo. Lo mejor sería un código de modo de depuración que sale del programa si asigna demasiada memoria, pero ¿cómo puedo hacer un seguimiento de la cantidad de memoria asignada? Anular los operadores globales nuevos y eliminar no ayudará, porque la función gratuita que invocaría en la eliminación no dará una idea de cuántos bytes se liberan.

Cualquier idea apreciada.

+1

¿Qué sistema operativo está utilizando? ¿Cuánta RAM? ¿Cuál es el tamaño del archivo de paginación? Esencial para saber –

+0

Si es propenso a este tipo de error, simplemente apague el intercambio. Tengo una máquina de desarrollo muy usada frente a mí con un tiempo de actividad de más de un mes. La marca de marea alta para el intercambio máximo utilizado es 22MiB. (linux 2.6.32 4GiB core, swapiness 60) – msw

Respuesta

10

Reemplazar los operadores globales nuevos y eliminar no ayudará, porque la función gratuita que invocaría en la eliminación no dará una idea de cuántos bytes se liberan.

Pero puede hacerlo así. Aquí está un marco completo para la sobrecarga de los operadores de la memoria global (tirarlo en algún archivo global_memory.cpp):

namespace 
{ 
    // utility 
    std::new_handler get_new_handler(void) 
    { 
     std::new_handler handler = std::set_new_handler(0); 
     std::set_new_handler(handler); 

     return handler; 
    } 

    // custom allocation scheme goes here! 
    void* allocate(std::size_t pAmount) 
    { 

    } 

    void deallocate(void* pMemory) 
    { 

    } 

    // allocate with throw, properly 
    void* allocate_throw(std::size_t pAmount) 
    { 
     void* result = allocate(pAmount); 

     while (!result) 
     { 
      // call failure handler 
      std::new_handler handler = get_new_handler(); 
      if (!handler) 
      { 
       throw std::bad_alloc(); 
      } 

      handler(); 

      // try again 
      result = allocate(pAmount); 
     } 

     return result; 
    } 
} 

void* operator new(std::size_t pAmount) throw(std::bad_alloc) 
{ 
    return allocate_throw(pAmount); 
} 

void *operator new[](std::size_t pAmount) throw(std::bad_alloc) 
{ 
    return allocate_throw(pAmount); 
} 

void *operator new(std::size_t pAmount, const std::nothrow_t&) throw() 
{ 
    return allocate(pAmount); 
} 

void *operator new[](std::size_t pAmount, const std::nothrow_t&) throw() 
{ 
    return allocate(pAmount); 
} 

void operator delete(void* pMemory) throw() 
{ 
    deallocate(pMemory); 
} 

void operator delete[](void* pMemory) throw() 
{ 
    deallocate(pMemory); 
} 

void operator delete(void* pMemory, const std::nothrow_t&) throw() 
{ 
    deallocate(pMemory); 
} 

void operator delete[](void* pMemory, const std::nothrow_t&) throw() 
{ 
    deallocate(pMemory); 
} 

entonces usted puede hacer algo como:

// custom allocation scheme goes here! 
    const std::size_t allocation_limit = 1073741824; // 1G 
    std::size_t totalAllocation = 0; 

    void* allocate(std::size_t pAmount) 
    { 
     // make sure we're within bounds 
     assert(totalAllocation + pAmount < allocation_limit); 

     // over allocate to store size 
     void* mem = std::malloc(pAmount + sizeof(std::size_t)); 
     if (!mem) 
      return 0; 

     // track amount, return remainder 
     totalAllocation += pAmount; 
     *static_cast<std::size_t*>(mem) = pAmount; 

     return static_cast<char*>(mem) + sizeof(std::size_t); 
    } 

    void deallocate(void* pMemory) 
    { 
     // get original block 
     void* mem = static_cast<char*>(pMemory) - sizeof(std::size_t); 

     // track amount 
     std::size_t amount = *static_cast<std::size_t*>(mem); 
     totalAllocation -= pAmount; 

     // free 
     std::free(mem); 
    } 
+0

Almacenar el tamaño en el espacio asignado es una buena idea. ¿Por qué no se me vino a la mente? También +1 por señalar que debería anular las versiones de lanzamiento y no lanzamiento de nuevo y eliminar. Las olvidé por completo. :) – Calmarius

1

porque la función libre, me gustaría invocar en el borrado no dar alguna idea de cuántos bytes se liberan

Es posible, sólo tendrá que mantener un mapa del tamaño del asignado memoria por dirección, y restar la cantidad correcta en función de esa información durante la gratuidad.

+0

El mapa STL usaría el nuevo global y también eliminaría a menos que le dieras tu propio asignador que fuera independiente del global. Veo la posibilidad de una recursión infinita. – Calmarius

0

Se puede aplicar es el propietario nuevo operador global:

void* operator new (std::size_t size) throw (std::bad_alloc); 
void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw(); 
void* operator new (std::size_t size, void* ptr) throw(); 

void* operator new[] (std::size_t size) throw (std::bad_alloc); 
void* operator new[] (std::size_t size, const std::nothrow_t& nothrow_constant) throw(); 
void* operator new[] (std::size_t size, void* ptr) throw(); 

A continuación, acaba de establecer un límite estricto sobre la cantidad de memoria que asigne; tal vez incluso cómo moch Kb/sec

13

Si está en un sistema Linux o Unix-ish, puede consultar en setrlimit(2) que le permite configurar los límites de recursos para su programa. Puede hacer cosas similares desde el shell con ulimit.

+0

+1 porque es otra cosa que no sabía. Pero quiero que mi programa sea multiplataforma, por lo que a veces lo desarrollo bajo Windows a veces bajo Linux y mi problema puede ocurrir en ambos lados. – Calmarius

0

Si desea una manera fácil de encontrar todos aquellos posibles fugas, simplemente use su editor de texto y busque .push_back en todo su código fuente. Luego examine todas las ocurrencias de esa llamada de función y vea si residen dentro de un circuito cerrado. Eso puede ayudarlo a encontrar algunos problemas graves en el código. Claro que puedes obtener 100 hits, pero eso se puede examinar en un tiempo finito. O bien, podría escribir un analizador estático (utilizando Scitools API) para buscar todos los bucles while que tienen un método de contenedor llamado .push_back que se llama dentro de ellos.

Cuestiones relacionadas