tracemalloc en Python: detecta fugas

Publicado el: 15/08/2026
Tempo de leitura: 6 minutos
Desarrollador investigando consumo de memoria con tracemalloc en Python

tracemalloc en Python permite descubrir dónde una aplicación asigna memoria. El módulo registra el origen de los bloques gestionados por el intérprete y permite comparar snapshots para localizar crecimiento inesperado, objetos retenidos y rutas de código que requieren investigación. Resulta especialmente útil en APIs, workers, procesos de datos y servicios que permanecen activos durante muchas horas.

Un aumento de memoria no siempre significa una fuga. El proceso puede mantener cachés, pools, buffers, módulos importados o índices globales de forma intencional. Por eso, tracemalloc no solo indica que la memoria creció: muestra qué archivos, líneas y pilas de llamadas contribuyeron al cambio.

Cómo funciona tracemalloc

Cuando el seguimiento está activo, Python registra el origen de cada bloque acompañado por su gestor de memoria. Cada traza puede incluir archivo, línea y una pila corta. Después se capturan snapshots y se agrupan las estadísticas.

import tracemalloc

tracemalloc.start()
valores = [str(numero) for numero in range(100_000)]
snapshot = tracemalloc.take_snapshot()

for dato in snapshot.statistics('lineno')[:10]:
    print(dato)

El agrupamiento por lineno destaca las líneas responsables de las mayores asignaciones rastreadas.

Activa el seguimiento al inicio

Si necesitas observar importaciones, inicialización del framework y construcción de cachés, inicia tracemalloc al comienzo del proceso. También puedes usar la variable PYTHONTRACEMALLOC.

PYTHONTRACEMALLOC=10 python app.py

El número determina cuántos frames se guardan por traza. Una profundidad mayor aporta contexto, pero consume más CPU y memoria.

Compara snapshots

El flujo más útil consiste en capturar una referencia, ejecutar varias veces la operación sospechosa y tomar otro snapshot.

tracemalloc.start(10)
antes = tracemalloc.take_snapshot()

for _ in range(50):
    procesar_lote()

despues = tracemalloc.take_snapshot()
for cambio in despues.compare_to(antes, 'lineno')[:20]:
    print(cambio)

La comparación muestra diferencia de tamaño, cantidad de bloques y ubicación. Un crecimiento repetido en la misma línea merece análisis.

Crea una carga reproducible

El diagnóstico es más fiable cuando la carga está controlada. Repite la misma operación con datos similares y evita mezclar despliegue, importaciones y calentamiento con la medición principal.

En una API, calienta rutas, serializadores y conexiones antes del snapshot inicial. En un worker, procesa algunos trabajos para estabilizar colas y cachés internas.

Filtra el ruido

Los snapshots incluyen código propio, bibliotecas, herramientas de prueba e infraestructura. Aplica filtros para concentrarte en el proyecto.

filtros = [
    tracemalloc.Filter(True, '*/mi_proyecto/*'),
    tracemalloc.Filter(False, '*/site-packages/*'),
]
filtrado = snapshot.filter_traces(filtros)

Comprueba los caminos reales del entorno, porque containers y entornos virtuales cambian los prefijos.

Agrupa por traceback

El agrupamiento por traceback muestra la secuencia de llamadas que produjo la asignación. Esto ayuda cuando una función auxiliar es utilizada por varias rutas.

for dato in snapshot.statistics('traceback')[:5]:
    print(dato)
    for linea in dato.traceback.format():
        print(linea)

Empieza con pocas capas y aumenta la profundidad solo si falta contexto.

Mide el uso actual y el pico

get_traced_memory() devuelve la memoria rastreada actualmente y el valor máximo observado desde el inicio.

actual, pico = tracemalloc.get_traced_memory()
print(actual / 1024 / 1024)
print(pico / 1024 / 1024)

El pico ayuda en tareas que liberan memoria al terminar, pero necesitan buffers grandes durante una fase intermedia.

Reinicia la referencia del pico

tracemalloc.reset_peak()
resultado = crear_informe()
actual, pico = tracemalloc.get_traced_memory()

Esto no elimina asignaciones ni reinicia el seguimiento. Solo redefine el máximo de referencia.

Colecciones globales sin límite

Un patrón frecuente es una lista global que crece durante toda la vida del proceso.

historial = []

def registrar(evento):
    historial.append(evento)

Tracemalloc señalará la línea del append. Puedes usar una cola limitada, almacenamiento externo o una política de expiración.

from collections import deque
historial = deque(maxlen=10_000)

Limita las cachés

Una caché ilimitada puede retener argumentos y resultados indefinidamente. Define un tamaño y observa su tasa de aciertos.

from functools import lru_cache

@lru_cache(maxsize=512)
def cargar_configuracion(clave):
    ...

Consulta cache_info() y limpia la caché cuando lo exija su ciclo de vida.

Callbacks que retienen objetos

Closures, listeners y callbacks pueden mantener estructuras grandes. Un callback registrado en un objeto global puede capturar datos sin intención. Revisa señales, registries, buses de eventos y futures pendientes.

La guía sobre weakref en Python explica cómo usar referencias débiles en cachés y observadores.

Generadores y tareas asíncronas

Los generadores suspendidos conservan frames y variables locales. Las tareas asyncio pendientes pueden retener contexto, excepciones, cuerpos de solicitud y buffers. Lista las tareas, inspecciona colas y espera correctamente las cancelaciones.

Para contexto asíncrono aislado, consulta contextvars en Python.

Excepciones y tracebacks

Guardar excepciones durante mucho tiempo puede preservar frames y variables locales. Evita almacenar objetos de excepción completos en listas globales. Serializa solo la información necesaria.

El artículo sobre traceback en Python muestra cómo registrar pilas sin retención innecesaria.

Lecturas grandes y buffers temporales

Una llamada a read() sin límite puede cargar un archivo completo. Prefiere streaming o bloques. Consulta tempfile en Python para patrones seguros con archivos temporales.

Tracemalloc no mide toda la memoria

El módulo rastrea principalmente asignaciones realizadas por el gestor de memoria de Python. La memoria nativa de extensiones C, bibliotecas numéricas, drivers o procesos hijos puede no aparecer completamente.

Compara los datos con RSS, métricas del sistema operativo y perfiles específicos. En cargas intensivas con NumPy, los buffers nativos pueden dominar el consumo.

Usa herramientas complementarias

Combina snapshots con métricas de proceso, logs, inspección de objetos y pruebas de carga. La documentación oficial de tracemalloc explica snapshots, filtros y trazas. La documentación de memoria de CPython describe sus asignadores.

Guarda snapshots

snapshot.dump('/tmp/memoria.snapshot')
cargado = tracemalloc.Snapshot.load('/tmp/memoria.snapshot')

Los archivos permiten análisis posterior, pero pueden revelar rutas y estructura interna del proyecto. Trátalos como datos de diagnóstico.

Crea pruebas de regresión

Una prueba puede repetir una operación y verificar que el crecimiento permanece dentro de un margen razonable.

def test_crecimiento_limitado():
    tracemalloc.start()
    antes = tracemalloc.take_snapshot()

    for _ in range(100):
        ejecutar_flujo()

    despues = tracemalloc.take_snapshot()
    crecimiento = sum(
        dato.size_diff
        for dato in despues.compare_to(antes, 'filename')
    )
    assert crecimiento < 5 * 1024 * 1024

No uses umbrales demasiado rígidos, porque versiones distintas de Python y dependencias pueden cambiar los patrones de asignación.

Precauciones en producción

El seguimiento añade overhead. Actívalo durante una ventana limitada, en una réplica de diagnóstico o con poca profundidad. No acumules snapshots. Elimina archivos temporales y llama a tracemalloc.stop() al terminar.

Lista de comprobación

  • Reproduce la carga en condiciones controladas.
  • Calienta la aplicación antes de la referencia.
  • Compara snapshots con el mismo agrupamiento.
  • Filtra dependencias ajenas al problema.
  • Revisa cachés, colecciones globales y registries.
  • Inspecciona tareas, generadores y excepciones retenidas.
  • Compara memoria rastreada, RSS y memoria nativa.
  • Valida la corrección con otra ejecución.

Conclusión

tracemalloc en Python convierte una sospecha de fuga en evidencia útil. Las comparaciones de snapshots, los filtros y las pilas de asignación muestran qué líneas crecen y ayudan a diferenciar cachés legítimas de retenciones accidentales.

Úsalo junto con métricas del sistema y pruebas reproducibles. Así obtendrás diagnósticos más rápidos, correcciones seguras y servicios Python de larga duración con un consumo de memoria predecible.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Desarrollador configurando un servidor HTTPS y certificado TLS con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crea un servidor HTTPS local en Python

    Aprende HTTPSServer en Python para crear servicios HTTPS locales, configurar certificados, usar hilos y comprender sus límites.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026