Vazamentos de memória Python [fechados]


180

Eu tenho um script de execução longa que, se for executado por tempo suficiente, consumirá toda a memória do meu sistema.

Sem entrar em detalhes sobre o script, tenho duas perguntas:

  1. Existem algumas "práticas recomendadas" a serem seguidas, que ajudarão a impedir a ocorrência de vazamentos?
  2. Quais técnicas existem para depurar vazamentos de memória no Python?

5
Eu encontrei esta receita útil.
David Schein

Parece para imprimir de forma demasiada de dados para ser útil
Casebash

1
@ Casebash: Se essa função imprime qualquer coisa, você está fazendo algo errado. Ele lista os objetos com o __del__método que não são mais referenciados, exceto pelo ciclo. O ciclo não pode ser interrompido devido a problemas com __del__. Consertá-lo!
Helmut Grohne em 02/11

Respostas:



83

Experimentei a maioria das opções mencionadas anteriormente, mas achei este pacote pequeno e intuitivo o melhor: pympler

É bastante simples rastrear objetos que não foram coletados pelo lixo, veja este pequeno exemplo:

instalar pacote via pip install pympler

from pympler.tracker import SummaryTracker
tracker = SummaryTracker()

# ... some code you want to investigate ...

tracker.print_diff()

A saída mostra todos os objetos que foram adicionados, além da memória que consumiram.

Saída de amostra:

                                 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 pacote fornece vários outros recursos. Verifique a documentação do pympler , em particular a seção Identificando vazamentos de memória .


5
Vale a pena notar que pymplerpode ser lento . Se você estiver fazendo algo semi-em tempo real, isso pode prejudicar completamente o desempenho do aplicativo.
Nome Falsificado

@sebpiq estranhamente, o mesmo acontece comigo ... você tem alguma idéia de por que isso está acontecendo? Uma rápida olhada no código fonte não forneceu informações reais.
linusg

25

Deixe-me recomendar a ferramenta mem_top que eu criei

Isso me ajudou a resolver um problema semelhante

Ele mostra instantaneamente os principais suspeitos por vazamento de memória em um programa Python


1
isso é verdade ... mas dá muito pouco em termos de uso / resultados explicação
me_

@me_, esta ferramenta possui as seções "Uso" e "Explicando resultado" documentadas. Devo adicionar explicações como "refs é contagem de referências do objeto, tipos é contagem de objetos desse tipo, bytes é o tamanho do objeto" - não seria óbvio demais documentar isso?
Denis Ryzhkov

os documentos de uso da ferramenta fornecem uma única linha dizendo "de tempos em tempos: logging.debug (mem_top ())", enquanto sua explicação dos resultados é a experiência de rastreamento de erros da vida real do autor sem contexto ... essa não é uma especificação técnica que informa um desenvolvedor exatamente para o que eles estão olhando ... Não estou respondendo à sua resposta ... mostra suspeitos de alto nível como faturados ... não fornece documentação adequada para compreender completamente o resultado do uso ... por exemplo , na saída "Explicando resultados", por que o "GearmanJobRequest" obviamente é um problema? nenhuma explicação para o porquê ...
me_

1
Eu acho que sou inadvertidamente bater sua ferramenta, você é o autor ... sem ofensa foi destinado ...
me_

6
@me_, acabei de adicionar a próxima etapa a "Uso", a seção "Contadores", a explicação de por que exatamente Gearman era suspeito nesse exemplo da vida real, documentou cada parâmetro opcional de "mem_top ()" no código, e carregou tudo isso como v0.1.7 - dê uma olhada se algo mais poderia ser melhorado. Obrigado! )
Denis Ryzhkov

18

O módulo Tracemalloc foi integrado como um módulo interno a partir do Python 3.4 e, aparentemente, também está disponível para versões anteriores do Python como uma biblioteca de terceiros (ainda não o testamos).

Este módulo é capaz de produzir os arquivos e linhas precisos que alocaram mais memória. IMHO, essas informações são infinitamente mais valiosas do que o número de instâncias alocadas para cada tipo (o que acaba sendo muitas tuplas 99% do tempo, o que é uma pista, mas dificilmente ajuda na maioria dos casos).

Eu recomendo que você use tracemalloc em combinação com pyrasite . 9 em 10, executar o snippet dos 10 melhores em um shell de pyrasite fornecerá informações e dicas suficientes para corrigir o vazamento em 10 minutos. No entanto, se você ainda não conseguir encontrar a causa do vazamento, o pyrasite-shell em combinação com as outras ferramentas mencionadas neste tópico provavelmente também fornecerá mais algumas dicas. Você também deve dar uma olhada em todos os auxiliares extras fornecidos pelo pyrasite (como o visualizador de memória).



12

Você deve observar especialmente seus dados globais ou estáticos (dados de longa duração).

Quando esses dados crescem sem restrição, você também pode obter problemas no Python.

O coletor de lixo só pode coletar dados, que não são mais referenciados. Mas seus dados estáticos podem conectar elementos de dados que devem ser liberados.

Outro problema pode ser os ciclos de memória, mas pelo menos em teoria o coletor de lixo deve encontrar e eliminar ciclos - pelo menos enquanto não estiverem conectados a alguns dados de longa duração.

Que tipos de dados de vida longa são especialmente problemáticos? Dê uma boa olhada em todas as listas e dicionários - eles podem crescer sem limite. Nos dicionários, você pode até não perceber o problema, pois quando você acessa os ditados, o número de chaves no dicionário pode não ter grande visibilidade para você ...


7

Para detectar e localizar vazamentos de memória para processos de execução longa, por exemplo, em ambientes de produção, agora você pode usar o stackimpact . Ele usa tracemalloc embaixo. Mais informações neste post .

insira a descrição da imagem aqui


4

Quanto às melhores práticas, fique de olho nas funções recursivas. No meu caso, tive problemas com recursão (onde não era necessário). Um exemplo simplificado do que eu estava fazendo:

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 dessa maneira recursiva não acionará a coleta de lixo e limpará os restos da função; portanto, toda vez que o uso da memória estiver crescendo, cada vez mais.

Minha solução foi extrair a chamada recursiva de my_function () e ter o main () quando deveria chamá-la novamente. dessa forma, a função termina naturalmente e limpa depois de si mesma.

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()

7
O uso da recursão dessa maneira também será interrompido se você atingir o limite de profundidade da recursão porque o Python não otimiza as chamadas de cauda. Por padrão, são 1000 chamadas recursivas.
Lie Ryan

3

Não tenho certeza sobre "Boas práticas" para vazamentos de memória em python, mas o python deve limpar sua própria memória pelo coletor de lixo. Então, principalmente, eu começaria verificando uma lista circular de alguns curtos, pois eles não serão recolhidos pelo coletor de lixo.


3
ou referências a objectos que estão a ser mantidos sempre, etc
mate b

3
Vocês podem fornecer exemplos de listas circulares e objetos que estão sendo guardados para sempre?
Daniel

2

Este não é um conselho exaustivo. Porém, o número um a ter em mente ao escrever com o objetivo de evitar vazamentos de memória no futuro (loops) é garantir que qualquer coisa que aceite uma referência a um retorno de chamada, armazene esse retorno como uma referência fraca.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.