- 1 1. Introducción
- 2 2. Fundamentos de la gestión de memoria de Python
- 3 3. Cómo comprobar el uso de memoria
- 4 4. Cómo optimizar el uso de memoria
- 5 5. Solución de problemas
- 6 6. Ejemplo práctico: Medición del uso de memoria en scripts de Python
- 7 7. Resumen y siguientes pasos
1. Introducción
Público objetivo
Este artículo está dirigido principalmente a principiantes y usuarios intermedios que utilizan Python a diario. Es especialmente útil para quienes desean verificar el uso de memoria de sus programas y optimizarlo.
Objetivo del artículo
Los objetivos de este artículo son los siguientes:
- Comprender el mecanismo de gestión de memoria de Python.
- Aprender métodos concretos para medir el uso de memoria.
- Dominar técnicas de optimización para reducir el uso de memoria.
Al comprender este contenido, podrá ayudar a mejorar el rendimiento de los programas Python.
2. Fundamentos de la gestión de memoria de Python
Cómo funciona la gestión de memoria
En Python, la gestión de memoria se lleva a cabo mediante dos mecanismos principales: el recuento de referencias y la recolección de basura.
Recuento de referencias
El recuento de referencias es un mecanismo que cuenta cuántas veces cada objeto es referenciado.
En Python, cuando se crea un objeto, su recuento de referencias se establece en 1. Cada vez que otra variable referencia ese objeto, el recuento aumenta; cuando la referencia se elimina, el recuento disminuye. Cuando el recuento llega a 0, el objeto se libera automáticamente de la memoria.
Ejemplo de código
import sys
a = [1, 2, 3] ## Se crea un objeto lista
print(sys.getrefcount(a)) ## Recuento de referencias inicial (normalmente 2, incluyendo referencias internas)
b = a ## Otra variable referencia el mismo objeto
print(sys.getrefcount(a)) ## El recuento de referencias aumenta
del b ## La referencia se libera
print(sys.getrefcount(a)) ## El recuento de referencias disminuye
Recolección de basura
La recolección de basura (Garbage Collection, GC) es un mecanismo que recupera memoria que no puede ser liberada por el recuento de referencias (especialmente referencias circulares). En Python, el recolector de basura incorporado funciona periódicamente y elimina automáticamente los objetos innecesarios.
El recolector de basura está especializado en la detección y liberación de referencias circulares, y resulta útil en situaciones como las siguientes:
class Node:
def __init__(self):
self.next = None
## Ejemplo de referencia circular
a = Node()
b = Node()
a.next = b
b.next = a
## En este estado, el recuento de referencias no llega a cero y la memoria no se libera
Si desea manipular el recolector de basura de forma explícita, puede controlar su comportamiento usando el módulo gc.
import gc
## Ejecutar el recolector de basura de forma forzada
gc.collect()
Riesgo de fugas de memoria
La gestión de memoria de Python es muy potente, pero no es perfecta. En particular, existen riesgos de fugas de memoria en situaciones como las siguientes:
- Referencias circulares están presentes pero el recolector de basura está desactivado.
- En programas de larga duración, los objetos innecesarios pueden permanecer en la memoria.
Para prevenir estos problemas, es importante diseñar evitando referencias circulares y eliminar explícitamente los objetos innecesarios.
Resumen de esta sección
- La gestión de memoria de Python se lleva a cabo mediante los mecanismos de recuento de referencias y recolección de basura.
- La recolección de basura es útil especialmente para resolver referencias circulares, pero es importante prevenir el consumo innecesario de memoria mediante un diseño adecuado.
- En la siguiente sección, se explicará cómo medir concretamente el uso de memoria.
3. Cómo comprobar el uso de memoria
Método básico
sys.getsizeof()
para confirmar el tamaño del objeto
Al usar la función getsizeof()
incluida en el módulo sys
de la biblioteca estándar de Python, se puede obtener el tamaño de memoria de cualquier objeto en bytes.
Ejemplo de código
import sys
## Verificar el tamaño de memoria de cada objeto
x = 42
y = [1, 2, 3, 4, 5]
z = {"a": 1, "b": 2}
print(f"tamaño de x: {sys.getsizeof(x)} bytes")
print(f"tamaño de y: {sys.getsizeof(y)} bytes")
print(f"tamaño de z: {sys.getsizeof(z)} bytes")
Puntos a considerar
sys.getsizeof()
permite obtener solo el tamaño del propio objeto; no incluye el tamaño de otros objetos referenciados (por ejemplo, los elementos dentro de una lista).- Para medir con precisión el uso de memoria de objetos de gran escala, se requieren herramientas adicionales.
Uso de herramientas de perfilado
Medición de memoria por función con memory_profiler
memory_profiler
es una biblioteca externa que permite medir detalladamente el uso de memoria de un programa Python por función. Facilita identificar fácilmente cuánto memoria consume cada parte del código.
Configuración
Primero, instala memory_profiler
:
pip install memory-profiler
Modo de uso
@profile
decorador permite medir el consumo de memoria por función.
from memory_profiler import profile
@profile
def example_function():
a = [i for i in range(10000)]
b = {i: i**2 for i in range(1000)}
return a, b
if __name__ == "__main__":
example_function()
En tiempo de ejecución, usa el siguiente comando:
python -m memory_profiler your_script.py
Ejemplo de salida
Line ## Mem usage Increment Line Contents
------------------------------------------------
3 13.1 MiB 13.1 MiB @profile
4 16.5 MiB 3.4 MiB a = [i for i in range(10000)]
5 17.2 MiB 0.7 MiB b = {i: i**2 for i in range(1000)}
Monitorear el uso total de memoria del proceso con psutil
psutil
es una biblioteca potente que permite monitorear el uso total de memoria de un proceso. Es útil cuando se desea conocer el consumo total de memoria de un script o aplicación específica.
Configuración
Instálalo con el siguiente comando:
pip install psutil
Modo de uso
import psutil
process = psutil.Process()
print(f"Uso total de memoria del proceso: {process.memory_info().rss / 1024**2:.2f} MB")
Características principales
- Posibilidad de obtener el uso de memoria del proceso actual en bytes.
- Permite obtener pistas de optimización mientras se monitorea el rendimiento del programa.
Seguimiento detallado de memoria
Rastrear asignaciones de memoria con tracemalloc
Al usar tracemalloc
de la biblioteca estándar de Python, se puede rastrear el origen de las asignaciones de memoria y analizar qué partes consumen más memoria.
Modo de uso
import tracemalloc
## Iniciar seguimiento de memoria
tracemalloc.start()
## Proceso que consume memoria
a = [i for i in range(100000)]
## Mostrar uso de memoria
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics("lineno")
print("[Uso de memoria]")
for stat in top_stats[:5]:
print(stat)
Usos principales
- Identificar los puntos problemáticos de asignación de memoria.
- Comparar múltiples procesos para encontrar oportunidades de optimización.
Resumen de esta sección
- Para comprender el uso de memoria en Python, existen muchas opciones, desde herramientas básicas como
sys.getsizeof()
hasta herramientas de perfilado comomemory_profiler
ypsutil
. - Si el consumo de memoria del programa es crítico, elige la herramienta adecuada y gestiona eficientemente.
- En la siguiente sección, se explicarán métodos concretos para optimizar el uso de memoria.
4. Cómo optimizar el uso de memoria
Selección de estructuras de datos eficientes
Reemplazo de listas por generadores
Las comprensiones de listas son convenientes, pero consumen mucha memoria al manejar grandes volúmenes de datos. En su lugar, usar generadores permite generar los datos de forma secuencial, reduciendo significativamente el uso de memoria.
Ejemplo de código
## Uso de lista
list_data = [i**2 for i in range(1000000)]
print(f"Tamaño de memoria de la lista: {sys.getsizeof(list_data) / 1024**2:.2f} MB")
## Uso de generador
gen_data = (i**2 for i in range(1000000))
print(f"Tamaño de memoria del generador: {sys.getsizeof(gen_data) / 1024**2:.2f} MB")
Al usar generadores, se puede reducir significativamente el uso de memoria.
Como alternativa a los diccionarios, collections.defaultdict
Los diccionarios de Python son útiles, pero consumen mucha memoria al manejar datos a gran escala. Usar collections.defaultdict
permite establecer valores predeterminados de manera eficiente y simplificar el procesamiento.
Ejemplo de código
from collections import defaultdict
## Diccionario normal
data = {}
data["key"] = data.get("key", 0) + 1
## Usar defaultdict
default_data = defaultdict(int)
default_data["key"] += 1
Gestión de objetos innecesarios
Eliminación explícita mediante la sentencia del
En Python, es posible eliminar manualmente los objetos innecesarios, lo que reduce la carga del recolector de basura.
Ejemplo de código
## Eliminar variables innecesarias
a = [1, 2, 3]
del a
Después de la eliminación, la variable a
se libera de la memoria.
Uso del recolector de basura
Con el módulo gc
, se puede ejecutar manualmente el recolector de basura, lo que permite resolver fugas de memoria causadas por referencias circulares.
Ejemplo de código
import gc
## Ejecución del recolector de basura
gc.collect()
Optimización mediante bibliotecas externas
Uso de NumPy y Pandas
NumPy y Pandas están diseñados para gestionar la memoria de manera eficiente. Especialmente al manejar grandes volúmenes de datos numéricos, usar estas bibliotecas permite reducir significativamente el uso de memoria.
Ejemplo de uso de NumPy
import numpy as np
## Lista Python
data_list = [i for i in range(1000000)]
print(f"Tamaño de memoria de la lista: {sys.getsizeof(data_list) / 1024**2:.2f} MB")
## Array NumPy
data_array = np.arange(1000000)
print(f"Tamaño de memoria del array NumPy: {data_array.nbytes / 1024**2:.2f} MB")
Se observa que los arrays de NumPy son más eficientes en memoria que las listas.
Prevención de fugas de memoria
Para prevenir fugas de memoria, es importante tener en cuenta los siguientes puntos.
- Evitar referencias circulares Diseñar los objetos para que no se referencien mutuamente.
- Control del alcance Tener en cuenta el alcance de funciones y clases para no dejar objetos innecesarios.
Resumen de esta sección
- Para optimizar el uso de memoria, es importante elegir estructuras de datos eficientes y eliminar adecuadamente los objetos innecesarios.
- Al aprovechar bibliotecas externas como NumPy o Pandas, se puede lograr una gestión de memoria aún más eficiente.
- En la siguiente sección, se explicará la solución de problemas útil para la resolución de casos reales.
5. Solución de problemas
Cómo abordar un aumento repentino del uso de memoria
Ajustar el recolector de basura
Si el recolector de basura no funciona correctamente, la memoria no necesaria no se libera y el uso puede aumentar rápidamente. Para resolver este problema, use el módulo gc
para ajustar el recolector de basura.
Ejemplo de código
import gc
## Recolector de basura: verificar el estado de funcionamiento
print(gc.get_threshold())
## Recolector de basura: ejecutar manualmente
gc.collect()
## Recolector de basura: cambiar la configuración (ejemplo: ajustar el umbral)
gc.set_threshold(700, 10, 10)
Revisar el ciclo de vida de los objetos
Algunos objetos pueden permanecer en memoria incluso después de que ya no sean necesarios. En ese caso, considere revisar el ciclo de vida del objeto y eliminarlo en el momento adecuado.
Fugas de memoria por referencias circulares
Resumen del problema
Las referencias circulares ocurren cuando dos o más objetos se referencian mutuamente. En ese caso, el recuento de referencias no llega a cero y el recolector de basura no puede liberar la memoria.
Soluciones
- Utilizar referencias débiles (módulo
weakref
) para evitar referencias circulares. - Ejecutar manualmente el recolector de basura para resolver las referencias circulares.
Ejemplo de código
import weakref
class Node:
def __init__(self, name):
self.name = name
self.next = None
a = Node("A")
b = Node("B")
## Evitar referencias circulares usando referencias débiles
a.next = weakref.ref(b)
b.next = weakref.ref(a)
Cuando la herramienta de perfilado de memoria no funciona
Error de memory_profiler
Al usar memory_profiler
, puede ocurrir que el decorador @profile
no funcione. Este problema se debe a que el script no se está ejecutando correctamente.
Soluciones
- Ejecute el script con la opción
-m memory_profiler
:
python -m memory_profiler your_script.py
- Asegúrese de que la función a la que se aplica el decorador está especificada correctamente.
Error de psutil
Si psutil
no puede obtener información de memoria, puede haber un problema con la versión de la biblioteca o el entorno.
Soluciones
- Verifique la versión de
psutil
e instale la última versión:
pip install --upgrade psutil
- Asegúrese de que está obteniendo la información del proceso de la manera correcta:
import psutil
process = psutil.Process()
print(process.memory_info())
Cómo abordar errores por falta de memoria
Resumen del problema
Al manejar datos a gran escala, el programa puede generar un error de falta de memoria (MemoryError
).
Soluciones
- Reducir el tamaño de los datos Eliminando datos innecesarios y usando estructuras de datos eficientes.
## Uso de generador
large_data = (x for x in range(10**8))
- Realizar procesamiento por lotes Dividiendo los datos en fragmentos pequeños para reducir la cantidad de memoria consumida en cada operación.
for chunk in range(0, len(data), chunk_size):
process_data(data[chunk:chunk + chunk_size])
- Utilizar almacenamiento externo Guardando los datos en disco en lugar de en memoria para procesarlos (p.ej., SQLite, HDF5).
Resumen de esta sección
- Utilice el recolector de basura y la gestión del ciclo de vida para controlar adecuadamente el uso de memoria.
- Si aparecen referencias circulares o errores de herramientas, pueden resolverse con referencias débiles y configuraciones correctas.
- Los errores por falta de memoria pueden evitarse revisando las estructuras de datos, procesando por lotes o utilizando almacenamiento externo.
6. Ejemplo práctico: Medición del uso de memoria en scripts de Python
Aquí se muestra un ejemplo concreto de cómo medir el uso de memoria dentro de un script de Python utilizando las herramientas y técnicas explicadas hasta ahora. A través de este ejemplo práctico, aprenderás a analizar y optimizar el uso de memoria.
Escenario de muestra: Comparación del uso de memoria entre listas y diccionarios
Ejemplo de código
El siguiente script mide el uso de memoria de listas y diccionarios usando sys.getsizeof()
y memory_profiler
.
import sys
from memory_profiler import profile
@profile
def compare_memory_usage():
## Creación de la lista
list_data = [i for i in range(100000)]
print(f"Uso de memoria de la lista: {sys.getsizeof(list_data) / 1024**2:.2f} MB")
## Creación del diccionario
dict_data = {i: i for i in range(100000)}
print(f"Uso de memoria del diccionario: {sys.getsizeof(dict_data) / 1024**2:.2f} MB")
return list_data, dict_data
if __name__ == "__main__":
compare_memory_usage()
Pasos de ejecución
memory_profiler
si no tienes instalado, ejecuta lo siguiente:
pip install memory-profiler
- Ejecuta el script con
memory_profiler
:
python -m memory_profiler script_name.py
Ejemplo de salida
Line ## Mem usage Increment Line Contents
------------------------------------------------
5 13.2 MiB 13.2 MiB @profile
6 17.6 MiB 4.4 MiB list_data = [i for i in range(100000)]
9 22.2 MiB 4.6 MiB dict_data = {i: i for i in range(100000)}
Uso de memoria de la lista: 0.76 MB
Uso de memoria del diccionario: 3.05 MB
De este ejemplo se observa que los diccionarios consumen más memoria que las listas. Esto proporciona criterios para elegir la estructura de datos adecuada según los requisitos de la aplicación.
Escenario de muestra: Monitoreo del uso total de memoria del proceso
Ejemplo de código
El siguiente script usa psutil
para monitorear en tiempo real el uso total de memoria del proceso.
import psutil
import time
def monitor_memory_usage():
process = psutil.Process()
print(f"Uso inicial de memoria: {process.memory_info().rss / 1024**2:.2f} MB")
## Simulación del consumo de memoria
data = [i for i in range(10000000)]
print(f"Uso de memoria durante el procesamiento: {process.memory_info().rss / 1024**2:.2f} MB")
del data
time.sleep(2) ## Esperar la ejecución del recolector de basura
print(f"Uso de memoria después de eliminar los datos: {process.memory_info().rss / 1024**2:.2f} MB")
if __name__ == "__main__":
monitor_memory_usage()
Pasos de ejecución
psutil
si no tienes instalado, ejecuta lo siguiente:
pip install psutil
- Ejecuta el script:
python script_name.py
Ejemplo de salida
Uso de memoria inicial: 12.30 MB
Uso de memoria durante el procesamiento: 382.75 MB
Uso de memoria después de eliminar datos: 13.00 MB
Con este resultado puedes observar el comportamiento cuando grandes cantidades de datos consumen memoria y cómo la memoria se libera al eliminar objetos innecesarios.
Puntos clave de esta sección
- Para medir el uso de memoria, es importante combinar adecuadamente herramientas (
sys.getsizeof()
,memory_profiler
,psutil
, etc.). - Visualizar el uso de memoria de estructuras de datos y del proceso completo permite identificar cuellos de botella y posibilita un diseño de programa más eficiente.

7. Resumen y siguientes pasos
Puntos clave del artículo
- Fundamentos de la gestión de memoria en Python
- Python utiliza el “conteo de referencias” y la “recolección de basura” para gestionar automáticamente la memoria.
- Para evitar problemas de referencias circulares, es necesario un diseño adecuado.
- Métodos para verificar el uso de memoria
sys.getsizeof()
puedes verificar el tamaño de memoria a nivel de objeto.memory_profiler
y @psutil
, puedes medir detalladamente el consumo de memoria de funciones o de todo el proceso.
- Métodos para optimizar el uso de memoria
- Al usar generadores y estructuras de datos eficientes (p. ej., arrays de NumPy), puedes reducir el consumo de memoria al procesar grandes volúmenes de datos.
- Eliminar objetos innecesarios y aprovechar el recolector de basura ayuda a prevenir fugas de memoria.
- Aplicación en ejemplos prácticos
- A través de código real, aprendimos los pasos para medir la memoria y los métodos de optimización.
- Practicaste la diferencia de uso de memoria entre listas y diccionarios, y ejemplos de monitoreo de memoria de todo el proceso.
Próximos pasos
- Aplicar en tu propio proyecto
- Incorpora los métodos y herramientas presentados en este artículo en tus proyectos cotidianos de Python.
- Por ejemplo, prueba @
memory_profiler
en scripts que manejan grandes volúmenes de datos para identificar los puntos con mayor consumo de memoria.
- Aprender una gestión de memoria más avanzada
- La documentación oficial de Python contiene información detallada sobre la gestión de memoria y el módulo
gc
. Si te interesa, consulta lo siguiente:
- Uso de herramientas y servicios externos
- En proyectos a gran escala, usar las