Inicio > C++, Cómo hacer, Visual Studio > Cómo detectar fugas de memoria con Visual C++

Cómo detectar fugas de memoria con Visual C++


Fugas de memoria (memory leaks), cosas horribles del mundo, uno de los más grandes enemigos a los que el programador de C++ se puede enfrentar.

Todos las conocemos, ¿verdad? Ubicas memoria dinámica y olvidas desubicarla:

int main()
{
  int* p = new int();

  return 0;
  // no se elimina p con un delete, así que
  // aquí hay una fuga de memoria
}

 

Obvio el caso anterior difícilmente ocurriría en la vida real. Era un ejemplo simple. En esa vida real cosas mucho más complicadas pueden ocurrir. Y encontrar en dónde se encuentra la fuga es una tarea titánica.

Visual C++ provee algunas herramientas para poder detectar fugas de memoria. Cuando corremos nuestro programa en modo de depuración.

En primer lugar, cuando trabajamos con ATL o MFC, tenemos una macro llamada DEBUG_NEW que sirve para dar información sobre el archivo y línea donde se invoca la creación dinámica. Para ello tendríamos que hacer algo como:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

e incluirlo en todos los archivos. DEBUG_NEW lleva un registro de todos los objetos creados de forma dinámica, con el nombre del archivo y la línea de donde fueron instanciados. Cuando el programa termina, se llama al Object Dump de todos los objetos (en el caso de aquellos que deriven de CObject, se manda a la versión de CObject::Dump), lo que hace que se muestre información en la ventana de "Output" del IDE de Visual C++. Cuando hay un memory leak, así se presentará.

memoryleak

En la figura anterior les pongo un ejemplo. Corrí mi programa y al cerrarlo me detectó una fuga de memoria. En el output podemos ver los "dumps" o volcado de memoria que hace el IDE (en mi caso, Visual C++ 9). En particular, notamos que el primer volcado me lo redirecciona al archivo lookupoptions.cpp. Esto es gracias a que emplée DEBUG_NEW como indiqué anteriormente. Por ello, al hacer doble click me lleva directamente a donde se ubicó el elemento.

Por supuesto el saber dónde se generó el objeto es una parte del problema. Pero no siempre resulta obvio. Por ejemplo, en la figura anterior se crea un objeto con new y se agrega a una colección. Ésta se usa a lo largo de la existencia de una ventana hija y posteriormente la colección se encarga de eliminar todos los objetos que contenga. Por lo que la eliminación del objeto está en otra parte del código, posiblemente sin estar relacionada. Si el problema no es ubicar el objeto en memoria, sino desubicarlo.

Para poder detectar esto, normalmente tenemos que saber bajo qué condiciones se está creando nuestro objeto. En el ejemplo anterior, el IDE marca la clase que genera el objeto, pero no marca bajo qué condición se crea (i.e. qué otro objeto mandó crear la colección antes mencionada). ¿Cómo podríamos saberlo? Un breakpoint sería buena opción. Pero mejor aún, un breakpoint bajo las condiciones en las que se da la fuga. Pues bien, MFC / Visual C++ provée un mecanismo para hacer esto.

Cada objeto que se crea de forma dinámica tiene un ID, que representa su número de creación. Cuando se hace el volcado de la memoria para detectar la fuga, se imprime este ID entre llaves. En la figura anterior se muestran encerradas en un círculo rojo. En este caso, el objeto fue el 27620° en ser creado. Por otro lado, existe una función, _CrtSetBreakAlloc que toma como parámetro un número que es precisamente el número de objeto creado (en el caso del ejemplo anterior, 27620). Esta función hace que cuando se vaya a crear el objeto cuyo número es el del parámetro (i.e. 27620) se mande llamar a AfxDebugBreak, lo cuál actúa como un breakpoint. En ese momento, podemos revisar el stack y demás variables para saber cuál es el entorno y contexto de la creación de mi objeto, facilitando la caza del bug.

Luego entonces, basta mandar llamar a _CrtSetBreakAlloc al mero inicio del programa (main si estamos en consola, WinMain si es una aplicación para Windows, y CWinApp::InitInstance si trabajamos con MFC.

Me pareció interesante, ya que recientemente tuve este problema y pude resolverlo de forma fácil así. MSDN tiene éste artículo que viene con técnicas más avanzadas para detectar fugas de memoria, y éste artículo que habla sobre lo que les acabo de comentar.

Espero esto ayude a eliminar esas cosas horribles llamadas Fugas de Memoria.

Categorías:C++, Cómo hacer, Visual Studio Etiquetas:
  1. Aún no hay comentarios.
  1. No trackbacks yet.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s