了解 python 中的引用计数
Understanding reference count in python
我正在尝试了解 python 中引用计数的工作原理。我创建了一个变量 x 并为其赋值 10。所以基本上 x 指向存储 class int (10) 对象的内存位置。现在,当我尝试获取 x 和 10 的引用计数时,我得到了两个不同的引用计数。如果 x 指向存储 10 的同一内存位置,那么为什么它们具有不同的引用计数?
>>> import sys
>>> sys.getrefcount(10)
12
>>> a = 10
>>> sys.getrefcount(10)
13
>>> sys.getrefcount(a)
11
当您直接调用 sys.getrefcount(10)
时,调用本身会增加引用计数。调用站点上有一份 10
的参考资料,至少还有一份参考资料,原因我记不清了。
更详细的答案:当您 运行 交互式提示中的语句时,该语句被编译成字节码,然后由解释器执行。字节码存储在一个 code
对象中,您可以通过使用 compile()
内置语句自己编译语句来检查该对象:
>>> a = 10
>>> c = compile('sys.getrefcount(10)', '<stdin>', 'single')
>>> c
<code object <module> at 0x7f4def343270, file "<stdin>", line 1>
我们可以使用dis
模块来检查编译后的字节码:
>>> dis.dis(c)
1 0 LOAD_NAME 0 (sys)
2 LOAD_ATTR 1 (getrefcount)
4 LOAD_CONST 0 (10)
6 CALL_FUNCTION 1
8 PRINT_EXPR
10 LOAD_CONST 1 (None)
12 RETURN_VALUE
可以看到CALL_FUNCTION
之前是字节码LOAD_CONST 10
。但是它怎么知道 10
是要加载的常量呢?实际的字节码指令是 LOAD_CONST(0)
,其中 0
是 table 常量的索引,常量存储在 code
对象中:
>>> c.co_consts
(10, None)
所以这是对 10
的新引用之一(暂时)所在的位置。
而如果我们这样做:
>>> c2 = compile('sys.getrefcount(a)', '<stdin>', 'single')
>>> dis.dis(c2)
1 0 LOAD_NAME 0 (sys)
2 LOAD_ATTR 1 (getrefcount)
4 LOAD_NAME 2 (a)
6 CALL_FUNCTION 1
8 PRINT_EXPR
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
而不是 LOAD_CONST
只是 LOAD_NAME
a
碰巧指向的任何东西。对象 10
本身在 code
对象的任何地方都没有被引用。
更新: 第二个引用的来源相当模糊,但它来自使用 Arena structure for efficient memory management of AST nodes and the like. The arena also maintains a list (as in an actual Python list
) of Python objects parsed in the AST, in the case of numbers that happens here: https://github.com/python/cpython/blob/fee96422e6f0056561cf74fef2012cc066c9db86/Python/ast.c#L2144 的 AST 解析器(其中 PyArena_AddPyObject
添加所述列表的对象)。 IIUC 这个列表的存在只是为了确保从 AST 解析的文字在某处至少有一个引用。
在用于编译和 运行ning 交互语句的实际 C 代码中,arena 没有被释放 until after the compiled statement has been executed,此时第二个额外的引用消失了。
我正在尝试了解 python 中引用计数的工作原理。我创建了一个变量 x 并为其赋值 10。所以基本上 x 指向存储 class int (10) 对象的内存位置。现在,当我尝试获取 x 和 10 的引用计数时,我得到了两个不同的引用计数。如果 x 指向存储 10 的同一内存位置,那么为什么它们具有不同的引用计数?
>>> import sys
>>> sys.getrefcount(10)
12
>>> a = 10
>>> sys.getrefcount(10)
13
>>> sys.getrefcount(a)
11
当您直接调用 sys.getrefcount(10)
时,调用本身会增加引用计数。调用站点上有一份 10
的参考资料,至少还有一份参考资料,原因我记不清了。
更详细的答案:当您 运行 交互式提示中的语句时,该语句被编译成字节码,然后由解释器执行。字节码存储在一个 code
对象中,您可以通过使用 compile()
内置语句自己编译语句来检查该对象:
>>> a = 10
>>> c = compile('sys.getrefcount(10)', '<stdin>', 'single')
>>> c
<code object <module> at 0x7f4def343270, file "<stdin>", line 1>
我们可以使用dis
模块来检查编译后的字节码:
>>> dis.dis(c)
1 0 LOAD_NAME 0 (sys)
2 LOAD_ATTR 1 (getrefcount)
4 LOAD_CONST 0 (10)
6 CALL_FUNCTION 1
8 PRINT_EXPR
10 LOAD_CONST 1 (None)
12 RETURN_VALUE
可以看到CALL_FUNCTION
之前是字节码LOAD_CONST 10
。但是它怎么知道 10
是要加载的常量呢?实际的字节码指令是 LOAD_CONST(0)
,其中 0
是 table 常量的索引,常量存储在 code
对象中:
>>> c.co_consts
(10, None)
所以这是对 10
的新引用之一(暂时)所在的位置。
而如果我们这样做:
>>> c2 = compile('sys.getrefcount(a)', '<stdin>', 'single')
>>> dis.dis(c2)
1 0 LOAD_NAME 0 (sys)
2 LOAD_ATTR 1 (getrefcount)
4 LOAD_NAME 2 (a)
6 CALL_FUNCTION 1
8 PRINT_EXPR
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
而不是 LOAD_CONST
只是 LOAD_NAME
a
碰巧指向的任何东西。对象 10
本身在 code
对象的任何地方都没有被引用。
更新: 第二个引用的来源相当模糊,但它来自使用 Arena structure for efficient memory management of AST nodes and the like. The arena also maintains a list (as in an actual Python list
) of Python objects parsed in the AST, in the case of numbers that happens here: https://github.com/python/cpython/blob/fee96422e6f0056561cf74fef2012cc066c9db86/Python/ast.c#L2144 的 AST 解析器(其中 PyArena_AddPyObject
添加所述列表的对象)。 IIUC 这个列表的存在只是为了确保从 AST 解析的文字在某处至少有一个引用。
在用于编译和 运行ning 交互语句的实际 C 代码中,arena 没有被释放 until after the compiled statement has been executed,此时第二个额外的引用消失了。