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

Как в Python устроен процесс выделения памяти для переменных?

3.0 Senior🔥 131 комментариев
#Python Core

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

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

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

Как в Python устроен процесс выделения памяти для переменных?

Процесс выделения памяти в Python тесно связан с системой управления объектами, сборкой мусора и внутренним устройством интерпретатора CPython.

Основной принцип: Everything is an Object

В Python всё является объектом, включая числа, строки, функции и сами переменные. Переменные — это не хранилища значений, а ссылки на объекты в памяти.

a = 42
# Не означает, что переменная a содержит 42
# Означает, что a указывает на объект int(42) в памяти

b = a  # b также указывает на тот же объект
print(id(a) == id(b))  # True — одинаковый адрес в памяти

Структура объекта в CPython

Каждый объект имеет:

  1. Reference count — количество ссылок на этот объект
  2. Type pointer — указатель на тип объекта (class)
  3. Value — фактические данные
import sys

obj = 42
print(sys.getrefcount(obj))  # количество ссылок
print(type(obj))              # <class 'int'>
print(id(obj))                # адрес в памяти

Процесс выделения памяти при создании объекта:

Шаг 1: Выделение памяти в heape Когда создаёте объект, Python запрашивает у ОС память в heap'е.

Шаг 2: Инициализация объекта Получена память → инициализируются ref_count (1), type и данные.

Шаг 3: Привязка переменной Переменная становится ссылкой на объект и увеличивает ref_count.

x = [1, 2, 3]  # выделена память для списка → ref_count = 1
y = x          # ref_count теперь 2
del y          # ref_count = 1
del x          # ref_count = 0 → объект удалён из памяти

Управление памятью: Reference Counting

Python использует reference counting как основной механизм:

  • Когда создаёте ссылку → ref_count += 1
  • Когда удаляете ссылку → ref_count -= 1
  • Когда ref_count == 0 → объект немедленно удаляется, память освобождается
import sys

a = 100
print(sys.getrefcount(a))  # 2 (сама переменная + аргумент getrefcount)

b = a
print(sys.getrefcount(a))  # 3 (a + b + getrefcount)

del b
print(sys.getrefcount(a))  # 2 снова

Проблема: Циклические ссылки

Reference counting имеет проблему с циклическими ссылками:

# Циклическая ссылка — объекты не удаляются!
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

a = Node(1)
b = Node(2)
a.next = b
b.next = a  # циклическая ссылка!

# ref_count(a) = 2, ref_count(b) = 2
# del a, del b — оба остаются в памяти!

Для этого существует Garbage Collector — вторая линия защиты:

import gc

print(gc.get_count())  # объекты, отслеживаемые сборщиком мусора
gc.collect()           # принудительно запустить GC

GC периодически ищет циклы и удаляет неиспользуемые объекты.

Интерпулирование: Small Object Allocator

Для часто используемых небольших объектов Python использует object pooling:

a = 256
b = 256
print(id(a) == id(b))  # True! один и тот же объект

c = 257
d = 257
print(id(c) == id(d))  # False! разные объекты

Почему? Для ускорения Python кэширует целые числа от -5 до 256.

Стек vs Heap

  • Stack: локальные переменные, параметры функций (быстро, автоматически)
  • Heap: объекты (медленнее, управляется GC)
def example():
    x = 10          # сначала на стеке, потом объект int в heap
    lst = [1, 2]    # сама переменная lst на стеке, список в heap
    return lst      # стек очищается, но объект в heap остаётся

Оптимизация памяти: Памяти-представления (Memoryviews)

Для больших объемов данных используйте более эффективные хранилища:

import array

# Список занимает много памяти на каждый элемент
list_arr = [1, 2, 3, 4, 5]

# Array занимает значительно меньше
array_arr = array.array('i', [1, 2, 3, 4, 5])

# NumPy — самый эффективный для числовых данных
import numpy as np
np_arr = np.array([1, 2, 3, 4, 5], dtype=np.int32)

Практические советы:

  1. Избегайте циклических ссылок или используйте weakref
  2. Используйте контекстные менеджеры (with) для явного освобождения ресурсов
  3. Профилируйте память: memory_profiler, objgraph
  4. Для больших данных выбирайте правильные структуры (NumPy, array)
  5. Помните о slots для сокращения памяти на объектах
Как в Python устроен процесс выделения памяти для переменных? | PrepBro