2010-12-21 14 views
6

Pasé los últimos días tratando de encontrar pérdidas de memoria en un programa que estamos desarrollando.Ayuda con comportamiento de memoria extraño. Buscando filtraciones tanto en mi cerebro como en mi código

En primer lugar, traté de usar algunos detectores de fugas. Después de solucionar algunos problemas, ya no encuentran fugas. Sin embargo, también estoy monitoreando mi aplicación usando perfmon.exe. El Monitor de rendimiento informa que "Bytes privados" y "Conjunto de trabajo - Privado" aumentan constantemente cuando se utiliza la aplicación. Para mí, esto sugiere que el programa está usando más y más memoria cuanto más tiempo se ejecute. Sin embargo, los recursos internos parecen ser estables, así que esto suena como una filtración hacia mí.

El programa está cargando una DLL en tiempo de ejecución. Sospecho que estas filtraciones o lo que sea que ocurran en esa biblioteca se purgan cuando la biblioteca está descargada, por lo tanto, no serán detectadas por los detectores de fugas. Utilicé DevPartner BoundsChecker y Virtual Leak Detector para buscar fugas de memoria. Ambos supuestamente detectan fugas en las DLL.

Además, el consumo de memoria está aumentando en pasos y esos pasos aproximadamente, pero no exactamente, coinciden con ciertas acciones de la GUI que realizo en la aplicación. Si se tratara de errores en nuestro código, deberían activarse cada vez que se realizan las acciones y no solo la mayor parte del tiempo.

Cada vez que me enfrento con tanta extrañeza, comienzo a cuestionar mis suposiciones básicas. Entonces me dirijo a ustedes, que lo saben todo, por sugerencias. ¿Hay algún defecto en mis suposiciones? ¿Tiene una idea de cómo solucionar un problema como este?

Editar:
Actualmente estoy usando Microsoft Visual C++ (x86) en Windows 7 64.

Edit2:
simplemente utilicé IBM Purificar a la caza de fugas. En primer lugar, enumera un 30% completo del programa como memoria filtrada. Esto no puede ser cierto. Supongo que es identificar toda la DLL como filtrada o algo así. Sin embargo, si busco nuevas fugas cada pocas acciones, informa las fugas que corresponden con el aumento de tamaño informado por el Monitor de rendimiento. Esto podría ser una causa de una fuga. Lamentablemente, solo estoy usando la versión de prueba de Purify, por lo que no me mostrará la ubicación real de esas filtraciones. (Estas fugas solo aparecen durante el tiempo de ejecución. Cuando el programa sale, no hay fugas de ninguna herramienta)

+0

Que sea simplemente la fragmentación del montón? – sharptooth

+0

@sharptooth: Posiblemente. No lo sé. ¿Cómo me enteraría? ¿No debería Windows o el compilador hacerse cargo de la fragmentación del montón? – bastibe

+0

La fragmentación del montón no es tan fácil: http://stackoverflow.com/q/1588922/57428 – sharptooth

Respuesta

3

La supervisión del uso de la memoria de la aplicación con PerfMon o el Administrador de tareas no es una forma válida de verificar si hay pérdidas de memoria. Por ejemplo, su tiempo de ejecución puede estar aferrándose a la memoria extra del sistema operativo para fines de asignación previa o debido a la fragmentación.

El truco, en mi experiencia, es el montón de depuración CRT. Puede solicitar información sobre todos los objetos en vivo, y el CRT proporciona funciones para comparar instantáneas.

http://msdn.microsoft.com/en-us/library/wc28wkas.aspx

2

Perfmon está bien para avisarle si está goteando, pero es primitivo. Hay productos comerciales que harán mucho mejor. Uso AQTime para el código C++ y es excelente: http://www.automatedqa.com/products/aqtime/

Le dirá la línea de código que asignó la memoria que se filtró.

3

Es difícil saberlo sin ver su código, pero hay maneras menos obvias de que se produzcan "filtraciones" en un programa C++, p.

  1. fragmentación de la memoria - si se están asignando diferentes tamaños de objetos todo el tiempo, entonces a veces no habrá un área contigua suficientemente grande de memoria libre y más tendrá que ser asignado desde el sistema operativo. Las asignaciones de este tipo no se liberarán al sistema operativo hasta que se libere toda la memoria en la asignación, por lo que los programas que se ejecutan durante mucho tiempo tenderán a crecer (en términos de espacio de direcciones utilizado) a lo largo del tiempo.

  2. olvidando tener un virtual en un caso base que tiene funciones virtuales - un gotcha muy común que conduce a fugas.

  3. usando punteros inteligentes, como shared_ptr, y tienen un objeto a un shared_ptr a un objeto; las herramientas de pérdida de memoria no suelen detectar este tipo de cosas.

  4. usando punteros inteligentes y obteniendo referencias circulares: debe usar, p. un weak_ptr en algún lugar para romper el ciclo.

En cuanto a las herramientas, no es purificar lo cual es bueno, pero caro.

1

Perfmon observa el número de páginas (4K) asignadas a su programa. Esos serán típicamente administrados por el administrador de montón. Por ejemplo, si presionar un botón requiere 3 asignaciones de 1 KB cada una, el administrador del montón tendrá que solicitar una nueva página las tres primeras veces. La cuarta vez, todavía le quedan 3 KB. Por lo tanto, no puede concluir que la presión de su botón debe tener un efecto externo visible en todo momento.

1

Tengo una técnica no tradicional para ayudar a encontrar una fuga sospechosa en el código, que he usado en innumerables ocasiones y que es muy efectiva. Claramente, no es la única o la mejor manera de encontrar fugas, pero es un truco que debes tener en tu bolsa.

Dependiendo de la profundidad de su conocimiento del código, puede tener un par de puntos sospechosos en mente. Lo que hice en el pasado es atacar esos puntos sospechosos por (lo que yo llamo) amplificar la fuga. Esto se hace simplemente poniendo un círculo alrededor de un punto sospechoso, por lo que se llama no una, sino muchas veces, generalmente miles, pero eso depende del tamaño de la asignación subyacente. El truco es saber dónde colocar el lazo. En general, desea subir la pila de llamadas a un punto donde dentro del ciclo se espera que toda la memoria asignada sea desasignada. En tiempo de ejecución, utilice perfmon para ver los bytes privados y el conjunto de trabajo, cuando se ha disparado, ha encontrado la fuga. Desde ese punto, puede reducir el alcance del bucle en la pila de llamadas para concentrarse en la fuga.

Considérese el siguiente ejemplo (cojo, ya que puede ser):

char* leak() 
{ 
    char* buf = new char[2]; 
    buf[0] = 'a'; 
    buf[1] = '\0'; 
} 

char* furtherGetResults() 
{ 
    return leak(); 
} 

std::string getResults(const std::string& request) 
{ 
    return furtherGetResults(); 
} 

bool processRequest(SOCKET client, const std::string& request) 
{  
    std::string results; 

    results = getResults(request); 

    return send(client, results.c_str(), results.length(), 0) == results.length(); 
} 

Su no siempre es fácil encontrar la fuga si el código se distribuye entre módulos independientes o incluso en dlls por separado. También es difícil de encontrar porque las fugas son muy pequeñas, pero con el tiempo pueden crecer.

Para empezar se puede poner el lazo alrededor de los getResults de llamada():

bool processRequest(SOCKET client, const std::string& request) 
{  
    std::string results; 

    for (size_t i = 0; i < 1000000; i++) { 
     results = getResults(request); 
    } 

    return send(client, results.c_str(), results.length(), 0) == results.length(); 
} 

Si los picos de uso de memoria entonces usted tiene la fuga, después de esta se mueve hacia abajo la pila de llamadas a getResults() , luego para obtener resultados adicionales() y así sucesivamente hasta que lo hayas clavado. Este ejemplo simplifica excesivamente la técnica, pero en el código de producción suele haber mucho más código en cada función llamada y es más difícil de restringir.

Esta opción puede no estar siempre disponible, pero cuando lo encuentra, encuentra el problema muy rápidamente.

Cuestiones relacionadas