python - python -X showrefcount 报告扩展的负引用计数
问题描述
当我在正在编写的扩展上运行带有标志的 cpython 时,当我从函数(使用宏)返回 None 时-X showrefcount
,它会报告负引用计数(例如)。[-5538 refs, 13503 blocks]
Py-RETURN_NONE
已知事实:
- 确切的计数在运行之间有所不同,但保持在相同的数量级内。
- 无论发生什么,它似乎都在缓慢发生;在引用计数变为负数之前,我需要调用扩展函数大约 50,000 次。
- 如果我们用 替换
Py_RETURN_NONE;
,Py_INCREF(Py_None); return Py_None;
它什么都不会改变。事实上,我们似乎可以添加任意数量的Py_INCREF(Py_None)
s 而不影响引用计数。 - 如果我们替换
Py_RETURN_NONE;
为return Py_None;
并且不增加引用计数,它会出现段错误(如预期的那样)。 - 如果我们用另一个值替换 None 返回,例如
PyLong_FromLong(0);
,问题就消失了。
这是什么原因?相关问题:为什么运行空脚本后引用计数不为零?
最小的例子:
用于 cpython 调试构建的构建命令
$ ./configure --with-pydebug --with-valgrind && make
扩展名.c
#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject *f(void) {
int manifest_bug = 1;
if (manifest_bug) {
Py_RETURN_NONE;
}
else {
return PyLong_FromLong(0);
}
}
static PyMethodDef functions[] = {
{"f", (PyCFunction)f, METH_NOARGS, "" },
{NULL, NULL, 0, NULL},
};
static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,
.m_name = "foo",
.m_size = -1,
.m_methods = functions,
};
PyMODINIT_FUNC PyInit_foo(void) {
return PyModule_Create(&module);
}
安装程序.py
from setuptools import setup, Extension
name="foo"
def main() -> None:
setup(
name=name,
version="0.0.0",
ext_modules=[ Extension(name, ["extension.c"]) ],
)
if __name__ == "__main__":
main()
测试.py
import foo
# With fewer than roughly this many iterations, the reference count
# generally remains positive. With more iterations, it becomes more negative
for i in range(50000):
foo.f()
解决方案
问题是由于使用较旧版本的 python 构建的扩展,并使用从最新版本的源代码编译的调试版本运行。未使用稳定 ABI编译的扩展(并声明为这样做)在 python 版本之间不兼容二进制。
[感谢 ead 提出的直接导致此解决方案的问题的评论。]
推荐阅读
- android - 默认值未设置为 TextInputLayout Android XML
- cron - 从 AppleScript 制作 cronjob
- android - Android Sqlite 中的 Select 查询中的自定义对象
- activemq-artemis - Apache ActiveMQ Artemis 如何调查消息是否丢失?
- ssms - 有没有办法使用 saltStack 创建 SQL Server xp_cmdshell_proxy_account
- mysql - 产品销售量与产品价格的倍数
- c - 如何异步等待进程终止并找出退出代码?
- email - 使用 SQL Server 发送邮件
- amazon-redshift - Redshift 物化视图不刷新或删除
- macos - MacOS 从 PDF 渲染图像(没有第三方渲染库)