下面是我的测试代码:
#! /usr/bin/python3
import gc
import ctypes
name = "a" * 50
name_id = id(name)
del name
gc.collect()
print(ctypes.cast(name_id, ctypes.py_object).value)
输出:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
在我看来,gc.collect()
应该清除变量name
及其值,
但是为什么gc.collect()
之后的name_id
可以得到值呢?
1条答案
按热度按时间t3irkdon1#
你不应该 * 期望 *
gc.collect()
在这里做 * 任何事情 *。gc
只是控制循环垃圾收集器,这是一个辅助垃圾收集器,因为CPython使用引用计数作为它的主内存管理策略。循环垃圾收集器处理引用周期,这里 * 没有引用周期 ,所以gc.collect
不会做任何事情。在我看来,gc.collect()应该清除变量名和它的值,
Python根本不是这样工作的。variable 不再存在于
del name
中,但是 object 继续存在,在这种情况下,是由于编译器优化。Python变量不像C变量,它们不是内存块, 它们是引用特定名称空间中的对象的名称 *。在任何情况下,反汇编代码都可以给予您了解以下内容:
所以,当你的代码块被编译时,CPython编译器注意到
"a"*50
可以被转换成一个常量,它就这样做了。它为代码对象存储常量,直到那个代码对象不再存在(在这个例子中,当解释器存在时)。因为这个代码对象将维护对这个字符串对象的引用,所以它将一直存在。所以,更明确地说:
还要注意Python的内存管理是复杂的并且相当不透明的。所有的对象都在一个私有管理的堆上处理。仅仅因为一个对象被“释放”并不意味着运行时不会简单地根据需要为相同类型(或者其他合适的类型)的对象重用这一点内存。看看这个:
请注意在这些情况下您是如何恢复 * 一些对象 * 的,因为ipython交互式shell一直在创建对象,并且内部堆乐于重用这些内存。
看看在一个更简单的REPL中会发生什么:
所以是的,正如人们所期望的那样,我们试图访问一部分内存,这部分内存不仅被内部堆回收,而且被Python进程释放,因此,我们得到了一个分段错误。