Analizando la memoria en Python

| Publicado: | Código fuente

Estoy haciendo un análisis de la memoria del cliente de Ubuntu One (particularmente unos módulos que conforman el SyncDaemon propiamente dicho), y me pareció tan copado que muestro acá parte de eso (aprendí el 99% de esto gracias a Guillo).

El análisis se basa en usar Heapy. Si a ubuntuone-client se lo levanta con --debug-heapy_monitor, el mismo hace (sacando el manejo de error):

import guppy.heapy.RM
guppy.heapy.RM.on()

Nada más. Entonces, lo ejecuté, y lo llevé a un estado en donde quería probar un par de cosas.

En otra terminal, ejecuté:

python -c "from guppy import hpy; hpy().monitor()"

Y eso me abrió una linea de comandos a través de la cual me conecté a mi programa Python que estaba ejecutando en la otra ventana (sí, a SyncDaemon):

<Monitor>
*** Connection 1 opened ***
<Monitor> lc
CID PID ARGV
 1 4451 ['bin/ubuntuone-syncdaemon', '--debug-heapy_monitor']
<Monitor> sc 1
Remote connection 1. To return to Monitor, type <Ctrl-C> or .<RETURN>
<Annex> h

Documented commands (type help <topic>):
========================================
close h help int isolatest q reset stat

<Annex> stat
Target overview
------------------------------------
target.sys.executable = /usr/bin/python
target.wd             = /home/facundo/canonical/u1-client/aq-memory-improvements
target.pid            = 4451
------------------------------------

Abrí una consola interactiva, y le dije que me tomara un snapshot del heap:

<Annex> int
Remote interactive console. To return to Annex, type '-'.
>>> h1 = hp.heap()

Luego tiré un archivo con contenido a Ubuntu One, volví a pedir el heap. Y miré las diferencias:

>>> h2 = hp.heap()
>>> h2.diff(h1)
Summary of difference operation (A-B).
      Count   Size
 A  204310 17394824
 B  204296 17393604
 A-B    14   1220 = 0.00701 % of B

Differences by kind, largest absolute size diffs first.
 Index Count   Size Cumulative % of B Kind (class / dict of class)
   0    3    468     468 0.00269 str
   1    2    272     740 0.00425 dict (no owner)
   2    2    152     892 0.00513 ubuntuone.syncdaemon.marker.MDMarker
   3    1     92     984 0.00566 ubuntuone.syncdaemon.action_queue.Upload
   4    1     56    1040 0.00598 unicode
   5    2     56    1096  0.0063 ubuntuone.syncdaemon.logger.mklog
   6    1     52    1148  0.0066 ubuntuone.syncdaemon.action_queue.MakeFile
   7    1     36    1184 0.00681 types.MethodType
   8    1     36    1220 0.00701 ubuntuone.syncdaemon.sync.FSKey
   9    0      0    1220 0.00701 dict of ubuntuone.syncdaemon.event_queue.MyReader
  10    0      0    1220 0.00701 ubuntuone.syncdaemon.event_queue.MyReader

Seguí haciendo unos experimentos, y llegó un punto en que ciertos elementos no deberían estar más en memoria, pero estaban. Lo más piola es que pude ver dónde:

>>> markers = h5[39]
>>> markers
Partition of a set of 241 objects. Total size = 18316 bytes.
 Index Count %   Size % Cumulative % Kind (class / dict of class)
   0  241 100  18316 100   18316 100 ubuntuone.syncdaemon.marker.MDMarker
>>> markers.referrers
Partition of a set of 2 objects. Total size = 6332 bytes.
 Index Count %   Size % Cumulative % Kind (class / dict of class)
   0    1 50   6280 99    6280 99 dict (no owner)
   1    1 50     52 1    6332 100 collections.deque
>>> markers.referrers[0].referrers
Partition of a set of 1 object. Total size = 136 bytes.
 Index Count %   Size % Cumulative % Kind (class / dict of class)
   0    1 100    136 100     136 100 dict of ubuntuone.syncdaemon.action_queue.DeferredMap
>>> markers.referrers[1].referrers
Partition of a set of 1 object. Total size = 520 bytes.
 Index Count %   Size % Cumulative % Kind (class / dict of class)
   0    1 100    520 100     520 100 dict of ubuntuone.syncdaemon.file_shelf.LRUCache

El segundo es un caché, y está todo bien, está acotado y cuando se suelte, se volarán. Pero el primer es un leak (del código nuestro, no de Python), y me confirmó lo que yo había visto por inspección del código.

Buenísimo el Heapy, :D

Comentarios Imprimir