为什么 IPython 在我删除列表时管理内存的方式与 CPython 不同?

Why does IPython manage memory differently than CPython when I delete a list?

我最近了解到,当你在 Python 中删除一个列表时,这个列表的引用会保存在一个数组中,并在你初始化一个新列表时弹出。

我在我的常规解释器中 运行 这个:

l = [1,2,3]
l_id = id(l)
del l
g = [1,2,3]
id(g) == l_id # True

正如预期的那样,我得到了正确的结果。

我在我的 IPython 解释器上尝试了同样的事情,但得到的却是 False。为什么会这样?好点了吗?

Python版本:v3.7.0:1bf9cc5093

Ipython版本:7.5.0

更新

它也发生在不同的列表中:

l = [1,2,3]
l_id = id(l)
del l
g = [1,2,3,4,5,6,7,8]
id(g) == l_id # True

而且它总是发生,这不仅仅是 运行dom 的事情,他们得到相同的引用

更新 2

我知道为什么会这样,我只是想知道为什么它只发生在纯 python 解释器上而不是在我的 ipython 上,以及这些方法中哪一种更适合内存管理

更新 3

由于我可以解释这些列表具有相同 ID 的原因,我无法理解为什么 ipython 和 python 之间存在差异。

查看List listobject.c的实现。

正如我们所见,有一个引用数组,称为 free_list。其中数组的值是销毁的列表对象,数组索引的计数numfree。我们可以看到,如果删除了超过 80 个列表,则下一个列表不会保存在数组中。所以从这些行我们可以说我的陈述对于任何新的 python 解释器都是正确的。

但我仍然找不到ipython这样工作的理由

首先,id() 与 Python 语言中的一般内存管理没有任何关系。但是,在 CPython(因此是 IPython REPL)中,它与原始内存位置具有一对一的对应关系。一些评论指出,这个问题在抽象上不一定有意义,但仅限于 IPython 和标准 CPython REPL 它似乎适用。

所有发生的事情是,在处理您的单元格块时,IPython 环境在幕后创建了一些额外的对象(包括列表对象)。由于 l 的原始内存 space 被 IPython 在幕后创建的一些列表占用,因此 CPython 分配器为 g.

对于额外对象的一些证据,请考虑以下在 CPython 和 IPython 中对垃圾收集器进行内省的实验 运行。

from gc import get_objects
orig = set(map(id, get_objects()))
l = [1,2,3]
l_id = id(l)
del l
g = [1,2,3]
final = set(map(id, get_objects()))
len(orig.symmetric_difference(final))  # 2 in CPython, 6-40+ in IPython