Fugas de memoria de Python

Tengo un script de larga ejecución que, si se deja correr el tiempo suficiente, consumirá toda la memoria de mi sistema.

Sin entrar en detalles sobre el script, tengo dos preguntas:

  1. ¿Hay alguna “Práctica recomendada” para seguir, que ayude a evitar que ocurran fugas?
  2. ¿Qué técnicas existen para depurar las memory leaks en Python?

Eche un vistazo a este artículo: Rastreo de memory leaks de python

Además, tenga en cuenta que el módulo de recolección de basura realmente puede tener indicadores de depuración establecidos. Mira la función set_debug . Además, consulte este código de Gnibbler para determinar los tipos de objetos que se han creado después de una llamada.

Probé la mayoría de las opciones mencionadas anteriormente, pero este paquete pequeño e intuitivo es el mejor: pympler

Es bastante sencillo rastrear objetos que no fueron recolectados en la basura, verifique este pequeño ejemplo:

instalar el paquete a través de pip install pympler

 from pympler.tracker import SummaryTracker tracker = SummaryTracker() # ... some code you want to investigate ... tracker.print_diff() 

La salida muestra todos los objetos que se han agregado, más la memoria que consumieron.

Salida de muestra:

  types | # objects | total size ====================================== | =========== | ============ list | 1095 | 160.78 KB str | 1093 | 66.33 KB int | 120 | 2.81 KB dict | 3 | 840 B frame (codename: create_summary) | 1 | 560 B frame (codename: print_diff) | 1 | 480 B 

Este paquete proporciona una serie de características más. Consulte la documentación de Pympler , en particular la sección Identificación de memory leaks .

Déjame recomendar la herramienta mem_top ,
Eso me ayudó a resolver un problema similar.

Simplemente muestra instantáneamente a los principales sospechosos de memory leaks en un progtwig Python.

Debería tener un vistazo especial a sus datos globales o estáticos (datos de larga duración).

Cuando estos datos crecen sin restricción, también puede obtener problemas en Python.

El recolector de basura solo puede recostackr datos, a los que ya no se hace referencia. Pero sus datos estáticos pueden conectar elementos de datos que deberían liberarse.

Otro problema puede ser los ciclos de memoria, pero al menos en teoría el recolector de basura debería encontrar y eliminar ciclos, al menos siempre y cuando no estén enganchados en datos de larga vida.

¿Qué tipos de datos de larga vida son especialmente problemáticos? Eche un vistazo a las listas y los diccionarios; pueden crecer sin ningún límite. En los diccionarios, es posible que ni siquiera veas los problemas, ya que cuando accedes a los dicts, es posible que la cantidad de claves en el diccionario no sea de gran visibilidad para ti …

El módulo Tracemalloc se integró como un módulo integrado a partir de Python 3.4 y, aparentemente, también está disponible para versiones anteriores de Python como biblioteca de terceros (aunque no lo he probado).

Este módulo puede generar los archivos y líneas precisos que asignaron la mayor cantidad de memoria. En mi humilde opinión, esta información es infinitamente más valiosa que la cantidad de instancias asignadas para cada tipo (que termina siendo una gran cantidad de tuplas el 99% del tiempo, lo cual es una pista, pero apenas ayuda en la mayoría de los casos).

Te recomiendo que uses tracemalloc en combinación con pyrasite . 9 veces de cada 10, ejecutar el fragmento de código top 10 en un shell de pyrasite le dará suficiente información y sugerencias para solucionar la fuga en 10 minutos. Sin embargo, si aún no puede encontrar la causa de la fuga, pyrasite-shell en combinación con las otras herramientas mencionadas en este hilo probablemente le dará más sugerencias. También debe echar un vistazo a todos los ayudantes adicionales proporcionados por pyrasite (como el visor de memoria).

Para detectar y localizar memory leaks para procesos de larga ejecución, por ejemplo, en entornos de producción, ahora puede usar stackimpact . Utiliza tracemalloc debajo. Más información en este post .

introduzca la descripción de la imagen aquí

No estoy seguro acerca de las “Mejores prácticas” para las memory leaks en python, pero python debería borrar su propia memoria con su recolector de basura. Así que, en primer lugar, comenzaría por revisar la lista circular de algunos cortos, ya que no serán recogidos por el recolector de basura.

Este no es un consejo exhaustivo. Pero lo primero que hay que tener en cuenta al escribir con la idea de evitar futuras memory leaks (bucles) es asegurarse de que todo lo que acepta una referencia a una callback debe almacenar esa callback como una referencia débil.

En cuanto a las mejores prácticas, preste atención a las funciones recursivas. En mi caso, tuve problemas con la recursión (donde no era necesario). Un ejemplo simplificado de lo que estaba haciendo:

 def my_function(): # lots of memory intensive operations # like operating on images or huge dictionaries and lists ..... my_flag = True if my_flag: # restart the function if a certain flag is true my_function() def main(): my_function() 

operar de esta manera recursiva no activará la recolección de basura y eliminará los rests de la función, por lo que cada vez que más uso de la memoria crece y crece.

Mi solución fue sacar la llamada recursiva de my_function () y tener el controlador main () cuando se debe volver a llamar. De esta manera la función termina naturalmente y se limpia después de sí misma.

 def my_function(): # lots of memory intensive operations # like operating on images or huge dictionaries and lists ..... my_flag = True ..... return my_flag def main(): result = my_function() if result: my_function()