Pérdidas de memoria de Python [cerrado]


180

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 guión, tengo dos preguntas:

  1. ¿Hay alguna "mejor práctica" a seguir, que ayudará a evitar que se produzcan fugas?
  2. ¿Qué técnicas existen para depurar las pérdidas de memoria en Python?

55
He encontrado esta receta útil.
David Schein

Parece imprimir demasiados datos para ser útiles
Casebash

1
@Casebash: si esa función imprime algo, realmente lo estás haciendo mal. Enumera objetos con un __del__método que ya no se hace referencia a excepción de su ciclo. El ciclo no se puede romper debido a problemas con __del__. ¡Arreglalo!
Helmut Grohne

Respuestas:



83

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

Es bastante sencillo rastrear objetos que no se recolectaron como basura, consulte este pequeño ejemplo:

instalar 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 pérdidas de memoria .


55
Vale la pena señalar que pymplerpuede ser LENTO . Si está haciendo algo semi-en tiempo real, puede afectar por completo el rendimiento de su aplicación.
Nombre falso

@sebpiq curiosamente, me pasa lo mismo ... ¿tienes alguna idea de por qué sucede esto? Un vistazo rápido al código fuente no dio una idea real.
linusg

25

Déjame recomendarte la herramienta mem_top que creé

Me ayudó a resolver un problema similar.

Muestra instantáneamente los principales sospechosos de pérdidas de memoria en un programa Python


1
eso es cierto ... pero da muy poco en cuanto a la explicación de uso / resultados
me_

@me_, esta herramienta tiene documentadas las secciones "Uso" y "Explicando el resultado". ¿Debo agregar una explicación como "refs es el recuento de referencias del objeto, los tipos son el recuento de objetos de este tipo, los bytes son el tamaño del objeto" - ¿no sería demasiado obvio documentar esto?
Denis Ryzhkov

los documentos de uso de la herramienta dan una sola línea que dice "de vez en cuando: logging.debug (mem_top ())", mientras que su explicación de los resultados es la experiencia de seguimiento de errores de la vida real del autor sin contexto ... esa no es una especificación técnica que dice un desarrollador exactamente lo que están mirando ... No estoy llamando su respuesta ... muestra a los sospechosos de alto nivel como facturados ... no proporciona la documentación adecuada para comprender completamente el resultado del uso ... por ejemplo , en la salida "Explicación de resultados", ¿por qué el "GearmanJobRequest" obviamente es un problema? no hay explicación de por qué ...
me_

1
Supongo que estoy inadvertidamente tocando su herramienta, usted es el autor ... no se pretendió ofender ...
me_

66
@me_, acabo de agregar el siguiente paso a "Uso", agregué la sección "Contadores", agregué la explicación de por qué exactamente Gearman era sospechoso en ese ejemplo de la vida real, documenté cada parámetro opcional de "mem_top ()" en el código, y subió todo esto como v0.1.7 - eche un vistazo si se puede mejorar algo más. ¡Gracias! )
Denis Ryzhkov

18

El módulo Tracemalloc se integró como un módulo incorporado a partir de Python 3.4, y aparentemente, también está disponible para versiones anteriores de Python como una 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 muchas tuplas el 99% del tiempo, lo cual es una pista, pero apenas ayuda en la mayoría de los casos).

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



12

Deberías echar un vistazo especialmente a tus datos globales o estáticos (datos de larga duración).

Cuando estos datos crecen sin restricciones, también puede tener problemas en Python.

El recolector de basura solo puede recopilar 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 pueden ser los ciclos de memoria, pero al menos en teoría el recolector de basura debería encontrar y eliminar ciclos, al menos siempre que no estén enganchados con algunos datos de larga duración.

¿Qué tipos de datos de larga duración son especialmente problemáticos? Eche un buen vistazo a las listas y diccionarios: pueden crecer sin límite. En los diccionarios puede que incluso no veas los problemas que vienen, ya que cuando accedes a los dictados, la cantidad de claves en el diccionario puede no ser de gran visibilidad para ti ...


7

Para detectar y localizar pérdidas de memoria 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 .

ingrese la descripción de la imagen aquí


4

En cuanto a las mejores prácticas, esté atento a las funciones recursivas. En mi caso, me encontré con problemas de recursión (donde no era necesario que hubiera). 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 desencadenará la recolección de basura y eliminará los restos de la función, por lo que cada vez que el uso de la memoria crece y crece.

Mi solución fue extraer la llamada recursiva de my_function () y tener el controlador main () cuando llamarla nuevamente. De esta manera, la función termina de forma natural 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()

77
El uso de la recursión de esta manera también se interrumpirá si alcanza el límite de profundidad de recursión porque Python no optimiza las llamadas de cola. Por defecto, esto es 1000 llamadas recursivas.
Mentira Ryan

3

No estoy seguro acerca de las "Mejores prácticas" para las pérdidas de memoria en Python, pero Python debería borrar su propia memoria mediante su recolector de basura. Así que principalmente comenzaría por buscar una lista circular de algunos cortos, ya que el recolector de basura no los recogerá.


3
o referencias a los objetos que se están manteniendo siempre, etc
Matt B

3
¿Pueden por favor proporcionar ejemplos de listas circulares y objetos que se guardan para siempre?
Daniel

2

Este no es un consejo exhaustivo. Pero una cosa importante a tener en cuenta al escribir con la idea de evitar futuras pérdidas de memoria (bucles) es asegurarse de que cualquier cosa que acepte una referencia a una devolución de llamada, debe almacenar esa devolución de llamada como una referencia débil.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.