O módulo tracemalloc no Python ajuda a descobrir onde um programa está alocando memória. Ele registra rastros das alocações feitas pelo interpretador e permite comparar snapshots para identificar crescimento inesperado, objetos persistentes e trechos de código que merecem investigação. É uma ferramenta especialmente útil em APIs, workers, rotinas de dados e aplicações de longa duração.
Um aumento de memória nem sempre é um vazamento. O processo pode manter caches legítimos, estruturas globais, buffers ou pools internos. Por isso, o objetivo do tracemalloc não é apenas mostrar que a memória cresceu, mas revelar quais linhas e módulos contribuíram para esse crescimento.
Como o tracemalloc funciona
Quando o rastreamento é ativado, o Python registra a origem de cada bloco de memória acompanhado pelo alocador do interpretador. Cada registro pode incluir o arquivo, a linha e uma pilha curta de chamadas. Depois, o programa coleta snapshots e calcula estatísticas.
import tracemalloc
tracemalloc.start()
dados = [str(numero) for numero in range(100_000)]
snapshot = tracemalloc.take_snapshot()
for item in snapshot.statistics('lineno')[:10]:
print(item)O agrupamento por lineno mostra as linhas que concentram mais memória rastreada. Também é possível agrupar por arquivo ou traceback.
Inicie o rastreamento cedo
Para capturar importações, inicialização de caches e configuração de frameworks, ative o tracemalloc no começo do processo. Você pode chamar tracemalloc.start() no código ou usar a variável de ambiente PYTHONTRACEMALLOC.
PYTHONTRACEMALLOC=10 python app.pyO número define quantos frames serão armazenados em cada traceback. Mais frames oferecem contexto, mas aumentam o custo de memória e CPU.
Compare snapshots
A técnica mais útil é capturar um snapshot de referência, executar a operação suspeita e capturar outro snapshot.
import tracemalloc
tracemalloc.start(10)
antes = tracemalloc.take_snapshot()
for _ in range(50):
processar_lote()
depois = tracemalloc.take_snapshot()
for diferenca in depois.compare_to(antes, 'lineno')[:20]:
print(diferenca)O resultado mostra a variação de tamanho, a quantidade de blocos e a localização associada. Crescimento repetido na mesma linha merece atenção.
Crie um cenário reproduzível
Diagnósticos de memória são mais confiáveis quando a carga é controlada. Execute a mesma operação várias vezes, use dados semelhantes e evite misturar deploy, importações e aquecimento da aplicação com a medição principal.
Em uma API, por exemplo, aqueça rotas e conexões antes do snapshot inicial. Em um worker, processe alguns lotes para estabilizar pools e caches.
Filtre ruído
Snapshots incluem alocações de bibliotecas, testes e infraestrutura. Use filtros para concentrar a análise no seu projeto.
filtros = [
tracemalloc.Filter(True, '*/meu_projeto/*'),
tracemalloc.Filter(False, '*/site-packages/*'),
]
snapshot_filtrado = snapshot.filter_traces(filtros)Filtros inclusivos e exclusivos podem ser combinados. Confirme os caminhos reais no ambiente, pois containers e ambientes virtuais alteram os prefixos.
Agrupe por traceback
O agrupamento por traceback mostra a sequência de chamadas que levou à alocação. Isso ajuda quando a mesma função utilitária é chamada por rotas diferentes.
for estatistica in snapshot.statistics('traceback')[:5]:
print(estatistica)
for linha in estatistica.traceback.format():
print(linha)Use poucos frames inicialmente. Aumente a profundidade somente quando a linha final não fornecer contexto suficiente.
Meça a memória atual e o pico
get_traced_memory() retorna a memória atualmente rastreada e o pico desde o início.
atual, pico = tracemalloc.get_traced_memory()
print(f'atual={atual / 1024 / 1024:.2f} MB')
print(f'pico={pico / 1024 / 1024:.2f} MB')O pico é útil para tarefas que liberam a memória ao final, mas consomem muito durante uma etapa intermediária.
Redefina o pico em operações isoladas
Use reset_peak() antes de uma etapa específica.
tracemalloc.reset_peak()
resultado = gerar_relatorio()
atual, pico = tracemalloc.get_traced_memory()Isso não limpa as alocações nem reinicia o rastreamento. Apenas redefine a referência do pico.
Exemplo de crescimento acidental
historico = []
def registrar_evento(evento):
historico.append(evento)
Uma lista global sem limite pode parecer inofensiva, mas cresce durante toda a vida do processo. O tracemalloc apontará a linha do append. A correção pode ser usar uma fila limitada, persistência externa ou política de expiração.
from collections import deque
historico = deque(maxlen=10_000)Caches também precisam de limites
functools.lru_cache é seguro quando possui maxsize. Um cache ilimitado pode reter argumentos e resultados indefinidamente.
from functools import lru_cache
@lru_cache(maxsize=512)
def carregar_configuracao(chave):
...Monitore cache_info() e limpe caches quando o ciclo de vida da aplicação exigir.
Referências mantidas por callbacks
Closures, listeners e callbacks podem manter objetos vivos. Um callback registrado em um objeto global pode capturar uma estrutura grande sem intenção. Examine registries, sinais, tarefas pendentes e handlers.
O artigo sobre weakref no Python mostra como referências fracas ajudam em caches e observadores.
Geradores e tarefas assíncronas
Geradores pausados mantêm seu frame e variáveis locais. Tarefas asyncio pendentes também podem reter contexto, exceções e buffers. Liste tarefas, verifique filas e confirme que cancelamentos são aguardados.
Para contexto assíncrono isolado, consulte contextvars no Python.
Tracebacks e exceções
Guardar exceções por muito tempo pode manter frames e variáveis locais vivos. Evite armazenar objetos de exceção completos em listas globais. Prefira serializar as informações necessárias.
Veja também traceback no Python para registrar pilhas sem reter memória desnecessariamente.
Arquivos temporários e buffers
Leituras com read() podem carregar arquivos inteiros. Prefira streaming e blocos quando o volume é grande. O guia de tempfile no Python mostra padrões seguros para arquivos temporários.
Tracemalloc não mede tudo
O módulo acompanha principalmente alocações feitas pelo gerenciador de memória do Python. Memória nativa usada por extensões C, bibliotecas numéricas, drivers ou processos filhos pode não aparecer por completo.
Compare os dados com a memória residente do processo, métricas do sistema operacional e ferramentas específicas. Em aplicações NumPy, por exemplo, buffers nativos podem dominar o consumo.
Use ferramentas complementares
Combine tracemalloc com métricas de RSS, perfis de objetos, logs e testes de carga. A documentação oficial do tracemalloc descreve snapshots, filtros e tracebacks. O guia de gerenciamento de memória do CPython explica os alocadores internos.
Salve snapshots para análise posterior
snapshot.dump('/tmp/memoria.snapshot')
carregado = tracemalloc.Snapshot.load('/tmp/memoria.snapshot')Isso permite comparar execuções, mas trate o arquivo como dado interno. Ele pode revelar caminhos e estrutura do projeto.
Teste regressões de memória
Você pode criar testes que executam uma operação repetidamente e verificam se o crescimento permanece dentro de uma margem.
def testar_crescimento_controlado():
tracemalloc.start()
antes = tracemalloc.take_snapshot()
for _ in range(100):
executar_fluxo()
depois = tracemalloc.take_snapshot()
crescimento = sum(
item.size_diff
for item in depois.compare_to(antes, 'filename')
)
assert crescimento < 5 * 1024 * 1024Evite limites rígidos demais, pois versões do Python e bibliotecas alteram padrões de alocação. Use o teste como sinal de regressão, não como prova absoluta.
Cuidados em produção
O rastreamento possui overhead. Ative por janela limitada, em uma réplica de diagnóstico ou com profundidade pequena. Não deixe snapshots frequentes acumularem memória. Remova dados temporários e desative com tracemalloc.stop() quando terminar.
Checklist de investigação
- Reproduza a carga em ambiente controlado.
- Aqueça a aplicação antes do snapshot inicial.
- Compare snapshots com o mesmo agrupamento.
- Filtre bibliotecas sem relação com o problema.
- Examine caches, listas globais e registries.
- Verifique tarefas, geradores e exceções retidas.
- Compare com RSS e memória nativa.
- Valide a correção com nova execução.
Conclusão
O tracemalloc no Python transforma suspeitas de vazamento em evidências concretas. Ao comparar snapshots, filtrar ruído e investigar as linhas que mais crescem, você consegue separar caches legítimos de retenções acidentais.
Use a ferramenta junto com métricas do sistema e testes reproduzíveis. O resultado é um diagnóstico mais rápido, correções mais seguras e aplicações de longa duração com consumo de memória previsível.







