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.pyEl 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 * 1024No 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.







