Подтвердить что ты не робот

Как сборщик мусора Python обнаруживает циркулярные ссылки?

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

Если это произойдет, мое понимание (возможно, ошибочное) заключается в том, что модуль gc действует как отказоустойчивый (я предполагаю), проходя через всю выделенную память и освобождая любые недоступные блоки.

Как Python обнаруживает и освобождает ссылки на циркулярную память перед использованием модуля gc?

4b9b3361

Ответ 1

Я думаю, что нашел ответ, который я ищу в некоторых ссылках, предоставленных @SvenMarnich в комментариях к исходному вопросу:

Контейнерные объекты представляют собой объекты Python, которые могут содержать ссылки на другие объекты Python. Списки, классы, кортежи и т.д. Являются контейнерами; Целые числа, строки и т.д. - нет. Таким образом, только объекты контейнера подвержены риску оказаться в круговой ссылке.

Каждый объект Python имеет поле - * gc_ref *, которое (я считаю) установлено равным NULL для неконтейнерных объектов. Для объектов контейнера он устанавливается равным количеству объектов non container, которые ссылаются на него

Любой объект-контейнер со значением * gc_ref * больше 1 (? Я бы подумал 0, но сейчас OK?) имеет ссылки, которые не являются объектами-контейнерами. Таким образом, они достижимы и удалены из рассмотрения недоступных островов памяти.

Любой объект-контейнер, доступный объекту, который, как известно, доступен (т.е. те, которые мы только что признали имеющими счетчик * gc_ref * больше 1), также не нужно освобождать.

Остальные объекты контейнера недоступны (кроме друг друга) и должны быть освобождены.

http://www.arctrix.com/nas/python/gc/ - это ссылка, обеспечивающая более полное объяснение http://hg.python.org/cpython/file/2059910e7d76/Modules/gcmodule.c - это ссылка на исходный код, в котором есть комментарии, далее объясняющие мысли, связанные с обнаружением круглых ссылок

Ответ 2

Как Python обнаруживает и освобождает ссылки на циркулярную память перед использованием модуля gc?

Это не так. Gc существует только для обнаружения и освобождения круговых ссылок. Некруглые ссылки обрабатываются путем пересчета.

Теперь, чтобы узнать, как gc определяет набор объектов, на которые ссылается любой данный объект, посмотрите на функцию gc_get_references в Modules/gcmodule.c. Соответствующий бит:

// Where `obj` is the object who references we want to find
traverseproc traverse;
if (! PyObject_IS_GC(obj))
    continue;
traverse = Py_TYPE(obj)->tp_traverse;
if (! traverse)
    continue;
if (traverse(obj, (visitproc)referentsvisit, result)) {
    Py_DECREF(result);
    return NULL;
}

Основная функция здесь tp_traverse. Каждый тип уровня C определяет функцию tp_traverse (или в случае объектов, которые не содержат никаких ссылок, например str), устанавливает ее на NULL). Одним из примеров tp_traverse является list_traverse, функция обхода для list:

static int
list_traverse(PyListObject *o, visitproc visit, void *arg)
{
    Py_ssize_t i;

    for (i = Py_SIZE(o); --i >= 0; )
        Py_VISIT(o->ob_item[i]);
    return 0;
}

Я вижу, что обнаружены круговые ссылки, за исключением случаев, когда задействованные объекты имеют метод __del__().

Вы правы. Детектор цикла Python может обнаруживать и собирать циклы, если они не содержат объектов с помощью метода __del__, поскольку интерпретатору не удастся безопасно удалить эти объекты (чтобы получить интуицию о том, почему это так, представьте себе у вас есть два объекта с методами __del__, которые ссылаются друг на друга. В каком порядке они должны быть освобождены?).

Когда объекты с методом __del__ участвуют в цикле, сборщик мусора будет вставлять их в отдельный список (доступный через gc.garbage), чтобы программист мог вручную "разобраться" с ними.

Ответ 3

Как Python обнаруживает и освобождает ссылки на циркулярную память перед использованием модуля gc?

Сборщик мусора Python (на самом деле не модуль gc, который является только интерфейсом Python для сборщика мусора) делает это. Таким образом, Python не обнаруживает и не освобождает ссылки на циркулярную память, прежде чем использовать сборщик мусора.

Python обычно освобождает большинство объектов, как только их число ссылок достигает нуля. (Я говорю "большинство", потому что он никогда не освобождает, например, небольшие целые числа или интернированные строки.) В случае циклических ссылок это никогда не происходит, поэтому сборщик мусора периодически ходит по памяти и освобождает объекты с циркулярной ссылкой.

Конечно, это все зависит от CPython. Другие реализации Python имеют разное управление памятью (Jython = Java VM, IronPython = Microsoft.NET CLR).