Los Desarrolladores que no amaban a los Bugs (2 de n)

Una de las ventajas de los lenguajes manejados es la cesión al CLR del manejo de la memoria. Sin embargo, esto no quiere decir que nuestras aplicaciones no vayan a tener fugas de memoria, simplemente que los escenarios en los que se pueden producir se ven drásticamente reducidos.

En algunos casos podemos notar que nuestras Apps están consumiendo más memoria de la que deberían, pero ¿cómo podemos encontrar el origen de la fuga de memoria?

Con WinDbg

Nota: Si no tienes WinDbg puedes obtenerlo instalando el Windows Driver Kit o mediante chocolatey con el mandato “chocolatey install windbg”

Para este ejemplo, supongamos que tenemos una App con un consumo de memoria normal nada más abrirla, pero que según navegamos por ella aumenta su consumo hasta llegar a cifras poco razonables:

Image_Memory_Normal

Image_Memory_Too_Much

Como se ve en las imágenes, partimos de 16MB y tras navegar un poco por la App alcanzamos casi 3GB sin que tengamos justificación para dicho incremento. Es lógico asumir que nuestro código puede tener una fuga de memoria pero… ¿dónde?

Nuestra primera opción para investigarlo es generar un volcado de memoria de la App en cuestión mediante el Task Manager (botón derecho -> Create dump file). Ahora podemos analizarlo con tranquilidad usando WinDbg:

Open_Dump

El primer paso es cargar en WinDbg SOS Debugging Extension (SOS.dll) para permitir que examine los entresijos de los objetos del CLR:

.load C:\windows\Microsoft.NET\Framework64\v4.0.30319\sos.dll

Tras esto, debemos decirle a WinDbg que los símbolos han cambiado (hemos añadido la información sobre el CLR) mediante el mandato “.reload”

Ahora ya podemos preguntarle a WinDbg qué está pasando con la memoria. Una buena forma de empezar es listar el consumo de memoria según el tipo de objeto. Esto podemos hacerlo con “!dumpheap –stat”.

WinDbg_Dumpheap_Stat

En este caso vemos que hay casi 3GB repartidos entre casi 30.000 arrays de byte. Cifra que coincide sospechosamente con objetos Book. Examinemos un poco más esos arrays con “!dumpheap –type Byte”.

WinDbg_Dumpheap_Type

Tras una larga lista de todos los Byte[] examinamos cualquiera de ellos para ver cómo han ido a parar ahí con “!gcroot 72d67eafb0”

WinDbg_Gcroot

Parece que esos arrays están en un Book que está dentro de una lista que pertenece a LibraryController que se llama desde MainPage. Aquí es donde entra en juego el conocimiento que tengamos de la App que estamos depurando. Si miramos el código de nuestra App, veremos que existe un pequeño problema por el cual al cargar una página, se insertan 99999 libros en vez de uno :P

Con Visual Studio 2013 Ultimate

Visual Studio nos lo pone mucho más fácil. Podemos tomar un volcado de memoria de la App pausando su ejecución y eligiendo “Debug -> Save dump as…”. Debemos hacer esto nada más arrancar la App y otra vez cuando la fuga de memoria se haya producido.

Save_dump_as

Si abrimos el segundo volcado con Visual Studio (File -> Open -> File) podemos analizar la memoria con la opción “Debug Managed Memory”.

VS_Debug_Managed_Memory

Ahora podemos hacer que Visual Studio compare este volcado con él que sacamos nada más ejecutar la App. Para ello seleccionamos el primer fichero de volcado en “Compare to”.

Compare

Tras hacer esto, Visual Studio nos muestra los cambios en la memoria. Podemos ver como se han creado casi 300.000 Book con un consumo de casi 3GB:

Comparison

Además, si hacemos click en él, podemos ver en la parte inferior de dónde viene. Al igual que WinDbg, nos informa de que está en una Lista propiedad de LibraryController que ha llamado MainPage, que es efectivamente donde estaba nuestro bug.

references

Feliz caza de fugas,

Pablo Carballude (@Carballude_es)