Существуют ли какие-либо проблемы с подсчетом ссылок/сборкой мусора Python при работе с кодом C?

Просто ради удовольствия я решил создать привязку схемы к libpython, чтобы вы могли встраивать Python в программах Scheme. Я уже могу обращаться к Python C API, но на самом деле я не думал об управлении памятью.

Принцип работы FFI mzscheme заключается в том, что я могу вызвать функцию, и если эта функция возвращает указатель на PyObject, то я могу автоматически увеличить счетчик ссылок. Затем я могу зарегистрировать финализатор, который будет уменьшать счетчик ссылок при сборке мусора для объекта Scheme. Я просмотрел документацию по подсчету ссылок и не увидеть какие-либо проблемы с этим на первый взгляд (хотя в некоторых случаях это может быть неоптимально). Есть ли какие-то ошибки, которые я упускаю?

Кроме того, у меня не получается разобраться в документации по циклическому сборщику мусора. . Какие вещи мне нужно иметь в виду здесь? В частности, как мне сообщить Python, что у меня есть ссылка на что-то, чтобы он не собирал ее, пока я ее использую?


person Jason Baker    schedule 29.05.2010    source источник


Ответы (2)


Ваша ссылка на http://docs.python.org/extending/extending.html#reference-counts — правильное место. Разделы документации «Расширение и внедрение» и «Python/C API» объясняют, как использовать C API.

Подсчет ссылок — одна из раздражающих частей использования C API. Основная проблема заключается в том, чтобы все было прямо: в зависимости от вызываемой вами функции API вы можете владеть или не владеть ссылкой на объект, который вы получаете. Будьте осторожны, чтобы понять, владеете ли вы им (и, следовательно, не можете забыть УДАЛИТЬ его или отдать тому, кто его украдет) или заимствуете его (и должны УДАЛИТЬ его, чтобы сохранить его и, возможно, использовать во время своей функции). Наиболее распространенные ошибки, связанные с этим: 1) неправильное запоминание того, владеете ли вы ссылкой, возвращаемой определенной функцией, и 2) уверенность в том, что вы можете безопасно заимствовать ссылку на более длительное время, чем вы есть.

Вам не нужно делать ничего особенного для циклического сборщика мусора. Он нужен только для исправления ошибки в подсчете ссылок и не требует прямого доступа.

person Mike Graham    schedule 29.05.2010
comment
Итак... Python использует подсчет ссылок и сборщик мусора для циклической структуры? Это довольно серьезный недостаток. Вид дизайна. В любом случае, это звучит так, как будто Джейсону будет намного веселее, если какие-либо значения, участвующие в цикле на стороне python, будут подвергаться схеме. - person Eli Barzilay; 29.05.2010
comment
Хорошая информация. До тех пор, пока я УВЕЛИЧИВАЮ все, когда получаю, и УМЕНЬШАЮ, когда закончу, все ли со мной будет в порядке? Или есть какие-то проблемы, с которыми я могу столкнуться? - person Jason Baker; 29.05.2010
comment
@Jason, только INCREF заимствовал ссылки. Некоторые функции возвращают новые ссылки, которые уже содержат INCREF. INCREF их приведет к утечке памяти. - person Virgil Dupras; 29.05.2010
comment
@Eli - я думаю, что есть люди, которые будут обсуждать это, но я соглашусь с тобой. Основное преимущество подсчета ссылок заключается в том, что он больше похож на RAII (деструкторы вызываются довольно быстро после финализации). Но никто не использует деструкторы, потому что они блокируют сборку мусора. - person Jason Baker; 29.05.2010
comment
@Eli, основной стратегией уничтожения объектов в CPython является подсчет ссылок. Поскольку подсчет ссылок не будет обрабатывать недоступные иным образом ссылочные циклы, он дополнен циклическим сборщиком мусора (необязательным, включенным по умолчанию). Это расширение необходимо для предотвращения утечек памяти в любой системе с подсчетом ссылок, допускающей произвольные ссылки. Это всего лишь конструктивный недостаток, поскольку подсчет ссылок является конструктивным недостатком (конечно, некоторые люди могут заявить об этом). - person Mike Graham; 29.05.2010
comment
@Jason, также люди не определяют __del__, потому что типичный немедленный сбор - это деталь реализации, на которую не следует полагаться; он отличается для разных реализаций языка Python и может измениться в будущих версиях CPython. - person Mike Graham; 29.05.2010
comment
@ Джейсон, нет, в этом вся беда! Иногда вы заимствуете ссылку (и вам нужно использовать INCREF, чтобы владеть ссылкой на объект), а иногда вы крадете ссылку, вызывая функцию и уже владея ею; В этом случае INCREFing может привести к утечке памяти. Точно так же, когда и если использовать DECREF, зависит от того, украла ли какая-либо функция вашу ссылку, что и происходит. Различные функции предоставляют или заимствуют ссылки, которые они возвращают, и крадут или заимствуют ссылки, которые они получают. Помните, что функция, которую вы используете, может стать источником ошибок. - person Mike Graham; 29.05.2010
comment
@Jason, docs.python.org/c-api/intro .html#reference-count-details обсуждает то, о чем я говорил в своем последнем комментарии. - person Mike Graham; 29.05.2010
comment
Майк, Джейсон: да, подсчет ссылок является недостатком дизайна — это именно то, что я имел в виду... Помимо того, что это точно такая же проблема, от которой были разработаны сборщики мусора (ну, подсчет ссылок делает проблему более организованной, но глядя на эти комментарии и различные условия владения, заимствования и т. д., это не очень помогает). В любом случае, помимо всего этого, хороший сборщик мусора также имеет преимущество в работе с большими блоками памяти, что в некоторых случаях может даже снизить общую стоимость выполнения. - person Eli Barzilay; 30.05.2010
comment
@Eli, ты не ясно выразился мне; Я думал, вы конкретно утверждаете, что увеличение подсчета ссылок с помощью Cyclefinder было проблемой дизайна. Нравится ли вам рефкаутинг — гораздо более важный вопрос, и скорее религиозный, чем технический. - person Mike Graham; 30.05.2010
comment
Майк: Да, я знаю. Это была субъективная полушутка. (Большинство причин для подсчета ссылок заключается в том, что он проще, чем сборщик мусора, поэтому вы можете видеть, как забавно может быть использование подсчета ссылок и получение дополнительного GC.) - person Eli Barzilay; 30.05.2010

Самая большая проблема, которую я знаю о подсчете ссылок и C API, — это __del__ вещь. Когда у вас есть заимствованная ссылка на что-то, вы думаете, что можете обойтись без INCREF, потому что вы не отказываетесь от GIL, пока используете эту ссылку. Но если вы в конечном итоге удалите объект (например, удалив его из списка), возможно, вы инициируете вызов __del__, который может удалить ссылку, которую вы заимствуете, из-под ваших ног. Очень сложно.

Если вы INCREF (а затем, конечно, DECREF) все заимствованные ссылки, как только вы их получите, не должно быть никаких проблем.

person Virgil Dupras    schedule 29.05.2010