← Назад к вопросам

К чему приводят нюансы работы с памятью в Python

1.7 Middle🔥 121 комментариев
#Архитектура и паттерны#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Нюансы работы с памятью в Python и их последствия

Python имеет ряд особенностей управления памятью, которые нередко приводят к утечкам, неожиданному поведению и проблемам с производительностью. Я столкнулся с этими проблемами много раз в production коде.

1. Циклические ссылки и сборка мусора

Проблема

class Node:
    def __init__(self):
        self.parent = None
        self.children = []

parent = Node()
child = Node()
parent.children.append(child)
child.parent = parent  # Циклическая ссылка!

del parent  # Объект НЕ удалится, пока не запустится GC

Следствие

  • Утечки памяти: объекты остаются в памяти дольше, чем нужно
  • Замедление: GC (сборка мусора) должна обнаруживать циклы
  • Неопределённое время жизни: когда точно удалится объект, неизвестно

Решение

import weakref

class Node:
    def __init__(self):
        self.parent = None
        self.children = []

parent = Node()
child = Node()
parent.children.append(child)
child.parent = weakref.ref(parent)  # Слабая ссылка

del parent  # Объект удалится сразу

2. Глобальные переменные и кэши

Проблема

cache = {}  # Глобальный кэш в модуле

def process_user(user_id):
    if user_id not in cache:
        user = fetch_from_db(user_id)
        cache[user_id] = user
    return cache[user_id]

# После обработки 1 млн пользователей кэш занимает ГБ памяти

Следствие

  • Неконтролируемый рост памяти: кэш растёт до бесконечности
  • Стейл данные: кэш может содержать устаревшую информацию
  • Утечки в долгоживущих процессах: в worker-процессах, daemon-потоках

Решение

from functools import lru_cache

@lru_cache(maxsize=1000)
def process_user(user_id):
    return fetch_from_db(user_id)

3. Замыкания и утечки переменных

Проблема

def create_handlers():
    handlers = []
    for i in range(1000):
        big_data = [0] * 1_000_000
        handlers.append(lambda x: big_data[0] + x)
    return handlers

handlers = create_handlers()
# Все 1000 обработчиков хранят ссылку на big_data!

Решение

def create_handlers():
    handlers = []
    for i in range(1000):
        big_data = [0] * 1_000_000
        handlers.append(lambda x, val=big_data[0]: val + x)
        del big_data
    return handlers

4. Слоты вместо dict

Проблема

class User:
    pass

users = [User() for _ in range(1_000_000)]
# Каждый объект имеет __dict__, даже если атрибутов нет

Решение

class User:
    __slots__ = ["id", "name", "email"]
    
    def __init__(self, id, name, email):
        self.id = id
        self.name = name
        self.email = email

5. Неправильная работа с файлами

Проблема

def process_files():
    files = [open(f"file_{i}.txt") for i in range(1000)]
    # Забыли закрыть файлы!

Решение

def process_files():
    for i in range(1000):
        with open(f"file_{i}.txt") as f:
            data = f.read()

6. Потоковая обработка больших данных

Проблема

with open("huge_file.csv") as f:
    lines = f.readlines()  # 5 GB в памяти!

Решение

with open("huge_file.csv") as f:
    for line in f:
        process(line)

Итоги

Нюансы памяти приводят к утечкам, деградации производительности и ошибкам в production. Самые частые проблемы — циклические ссылки, глобальные кэши и неправильная работа с ресурсами.