Когда Py_INCREF? - PullRequest
       16

Когда Py_INCREF?

0 голосов
/ 14 мая 2018

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

До сих пор методом проб и ошибок я узнал, что, например, Py_DECREF(Py_None) - это плохо ... но верно ли это для любой константы?Я не знаю.

Мои основные недоразумения до сих пор можно перечислить следующим образом:

  1. Должен ли я уменьшить счет на что-либо, созданное PyWhatever_New(), если это не так?пережить процедуру, которая его создала?
  2. Нужно ли каждому Py_INCREF соответствовать Py_DECREF, или должен быть еще один из одного / другого?
  3. Если вызов Pythonпроцедура привела к PyObject*, нужно ли мне увеличивать его, чтобы гарантировать, что я все еще могу использовать его (навсегда), или уменьшать его, чтобы в конечном итоге он собирал мусор, или ни того, ни другого?
  4. Python объекты, созданные через C API в стеке, размещенном в стеке или в куче?(Возможно, что Py_INCREF перераспределяет их, например, в куче).
  5. Нужно ли делать что-то особенное с объектами Python, созданными в коде C, перед передачей их в код Python?Что, если код Python переживает код C, который создал объекты Python?
  6. Наконец, я понимаю, что в Python есть и счетчик ссылок, и сборщик мусора: если это так, насколько важно, если я испорчу счетчик ссылок (т.е.недостаточно декрементно), GC в конце концов выяснит, что делать с этими объектами?

1 Ответ

0 голосов
/ 14 мая 2018

Большая часть этого описана в Подробности подсчета ссылок , а остальное - в документации по конкретным вопросам, которые вы задаете. Но, чтобы получить все это в одном месте:

Py_DECREF(Py_None) это плохо ... но верно ли это для любой константы?

Более общее правило заключается в том, что вызов Py_DECREF для всего, на что вы не получили новую / украденную ссылку и не вызывал Py_INCREF, является плохой вещью. Поскольку вы никогда не вызываете Py_INCREF для чего-либо доступного в качестве константы, это означает, что вы никогда не вызываете Py_DECREF для них.

Нужно ли уменьшать refcount для всего, что создано PyWhatever_New()

Да. Все, что возвращает «новую ссылку», должно быть уменьшено. По соглашению все, что заканчивается на _New, должно возвращать новую ссылку, но в любом случае это должно быть задокументировано (например, см. PyList_New).

Нужно ли сопоставлять каждому Py_INCREF значение Py_DECREF, или должно быть еще одно из одного / другого?

Число в вашем собственном коде может не обязательно совпадать. Число total должно быть сбалансировано, но в самом Python происходят приращения и убывания. Например, все, что возвращает «новую ссылку», уже сделало inc, в то время как все, что «украдет» ссылку, будет делать dec для нее.

Объекты Python, созданные с помощью C API, размещены в стеке в стеке или в куче? (Возможно, что Py_INCREF перераспределяет их в куче, например).

Нет способа создавать объекты через C API в стеке. В C API есть только функции, которые возвращают указатели на объекты.

Большинство этих объектов расположены в куче. Некоторые на самом деле находятся в статической памяти.

Но ваш код все равно не должен волновать. Вы никогда не размещаете и не удаляете их; они распределяются в PySpam_New и аналогичных функциях и освобождают себя, когда вы Py_DECREF переводите их в 0, поэтому вам не важно, где они находятся.

(Исключением являются константы, к которым вы можете обращаться через их глобальные имена, например Py_None. Те, что, как вы знаете, находятся в статическом хранилище.)

Нужно ли делать что-то особенное с объектами Python, созданными в коде C, прежде чем передавать их в код Python?

номер

Что если код Python переживает код C, создавший объекты Python?

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

Если вы просто имеете в виду функцию, которая _New возвращает объект, это не проблема. Вы должны очень далеко уйти от размещения любых объектов Python в стеке. И нет никакого способа передать такие вещи, как массив объектов C или строку C, в код Python, не преобразовав их в набор объектов Python, или байты Python или str. Есть несколько случаев, когда, например, вы могли бы сохранить указатель на что-либо в стеке в PyCapsule и передать это - но это то же самое, что и в любой программе на C, и ... просто не делайте этого.

Наконец, я понимаю, что в Python есть счетчик ссылок и сборщик мусора

Сборщик мусора - просто прерыватель цикла. Если у вас есть объекты, которые поддерживают друг друга с помощью ссылочного цикла, вы можете положиться на GC. Но если вы пропустили ссылки на объект, GC никогда не очистит его.

...