Создание и удаление объектов. Сборщик мусора
Contents
Создание и удаление объектов. Сборщик мусора#
Итого, в python
все данные представляются в виде объектов, доступ к которым осуществляется или через имена/ссылки/идентификаторы в исходном коде программы. Разберемся, как эти объекты создаются и удаляются.
Создание объектов#
Создаются объекты в результате вычисления выражений python
. Рассмотрим следующую строку кода.
x = 1001 + int("1002")
Когда интерпретатор исполняет исходный код, он сначала вычисляет выражение справа от оператора “=
”.
1001 + int("1002")
Так как это составное выражение, то, чтобы его вычислить, необходимо сначала вычислить результаты составляющих его подвыражений 1001
и int("1002")
.
Первое из них (
1001
) является литералом целого числа 1001. Его интерпретация приводит к созданию целочисленного объекта со значением 1001.Второе выражение (
int("1002")
) тоже является составным.Сначала вычисляется выражение
"1002"
, указанное в качестве аргумента функцииint
. Это выражение является литералом строки, а значит его интерпретация приводит к созданию строкового объекта со значением"1002"
.Далее этот объект передаётся на вход функции
int
, что приводит к созданию целочисленного объекта со значением1002
.
Затем интерпретатор применяет операцию сложения к объектам целочисленного типа, что приводит к созданию ещё одного целочисленного объекта со значением 2003.
print(x)
2003
Далее имя x
связывается с этим объектом. Итого, в результате вычисления этой строки кода была создано 4 объекта.
Но как объекты в python
удаляются? Прежде чем объяснить принцип удаления объектов из памяти, рассмотрим ключевой механизм для этого процесса.
Механизм подсчета ссылок на объект#
Python
автоматически следит за тем, сколько ссылок существует ссылок на каждый объект в любой момент времени. Для этого каждый объект имеет специальное целочисленное поле, которое используется в качестве счетчика ссылок на этот объект. Каждый раз, когда на объект появляется новая ссылка, этот счетчик увеличивается на единицу.
Продемонстрировать этот эффект можно с помощью функции getrefcount из модуля стандартной библиотеки sys, которая как раз и возвращает количество ссылок на объект.
Note
Важно учесть, что когда мы передаем объект на вход функции getrefcount
, внутри её локального пространства имён создаётся ссылка на этот объект, из-за чего она возвращает значение на 1 больше, чем можно было ожидать.
Так как python
проводит ряд оптимизация при работе с неизменяемыми объектами (таких, как интернирование строк и малых целых чисел), то для чистоты эксперимента создадим изменяемый объект — пустой список, свяжем его с именем a
и выведем количество ссылок на него.
from sys import getrefcount
a = []
print(getrefcount(a))
2
Видим, что количество ссылок на этот объект равняется двум: одна из этих ссылок — имя a
, другая — временная ссылка, созданная внутри функции getrefcount
при передаче этого объекта в качестве аргумента.
Создадим ещё пару ссылок на этот объект и снова напечатаем количество ссылок на список.
b = a
print(getrefcount(a))
c = a
print(getrefcount(a))
3
4
Видим, что счетчик вырастает на 1, каждый раз, когда мы связываем новое имя с исходным объектом.
Кроме имен программы на объект могут ссылаться другие объект (или даже тот же самый в предельном случае). Например, список хранит в себе ссылки на свои элементы.
L = [a, a, a]
print(getrefcount(a))
7
Созданный список L=[a, a, a]
содержит 3 ссылки на исходный список, что отражается на увеличении счетчика ссылок.
Также временные ссылки создаются при передаче объекта в качестве аргумента функции. Именно этим и объясняется увеличенное значение, возвращаемое функцией getrefcount
.
Удаление объектов в python
. Ключевое слово del
#
Механизм подсчета ссылок используется для определения того, какие объекты можно удалять. Действительно, если ссылок на объект нет, то значит он недостижим для программы и его можно смело удалять.
Чтобы счетчик ссылок обнулился, необходимо чтобы пропали все ссылки на этот объект. Такое может происходить по разным причинам. Например, если связать какое-то существующее в программе имя с новым объектом, то пропадёт одна ссылка на объект, на которое это имя ссылалось прежде.
b = 0
print(getrefcount(a))
6
Видим, что связывание имени b
с целочисленным объектом 0
привело к тому, что количество ссылок на пустой список, на который ссылалось имя b
прежде, упало на 1.
Кроме того с помощью ключевого слова del
можно просто удалить имя из программы, а вместе с этим и ссылку на объект, на которое это имя ссылается. Чтобы удалить имя name
из текущего пространства имён, используется следующий синтаксис.
del name
Удалим имя c
и убедимся, что количество ссылок упадёт ещё на 1.
del c
print(getrefcount(a))
5
Локальные для функции имена удаляются автоматически при выходе из функции.
Если удаляется объект, то удаляются все его ссылки на другие объект. Чтобы продемонстрировать этот эффект, удалим список L
и убедимся, что количество ссылок на a
упадёт на 3.
del L
print(getrefcount(a))
2
Сборщик мусора#
На первый взгляд может показаться, что механизма подсчета ссылок хватает самого по себе для удаления всех недостижимых объектов, но это не так. Может сложиться патологическая ситуация, когда группа объектов циклически ссылается друг на друга, но ни на один из объектов этой группы нет ссылки снаружи. Такие объекты недостижимы для программы, а значит по-хорошему их пора удалять, но на каждый из этих объектов все ещё существуют ссылки.
Воспроизведем такую ситуацию на примере двух списков.
L1 = []
L2 = []
L1.append(L2)
L2.append(L1)
print(L1, L2)
[[[...]]] [[[...]]]
Схема ниже иллюстрирует сложившуюся ситуацию.
Удалим изначальные ссылки L1
и L2
.
del L1, L2
Теперь пропали все внешние ссылки на циклическую группу этих списков, т.е. они недостижимы для программы, но сами списки все ещё ссылаются друг на друга.
Чтобы обнаруживать такие ситуации, в python
предусмотрен специальный механизм — сборщик мусора — отдельный процесс, который периодически запускается и занимается поиском недостижимых циклических структур и удаляет их.
Положительная сторона такого подхода заключается в том, что удаление объектов происходит полностью автоматически и у программиста нет необходимости удалять все объекты самостоятельно. Отрицательная — накладные расходы: помимо интересующей пользователя программы параллельно работает посторонний процесс, который может вызвать небольшие замедления в её работе.
Note
Модуль gc предоставляет интерфейс к взаимодействию со сборщиком мусора. В частности, можно отключить автоматическую сборку мусора методом disable, например, если сценарий программы не подразумевает создание циклических структур, или если стабильная производительность программы важнее потенциальной утечки памяти. Кроме отключения сборщика мусора, с помощью этого модуля можно в ручную его запускать методом collect.
Note
Подробно прочитать о том, как работает сборщик мусора в python
можно по ссылке в документации.