当嵌套dict被保留/引用但外部dict被删除和垃圾回收时的Python内存行为

xghobddn  于 2023-05-21  发布在  Python
关注(0)|答案(1)|浏览(152)

我试图在嵌套的dicts上下文中理解Python中的内存使用。我有以下的命令:

>>> outer = {
...     'name': 'John Smith',
...     'books': [
...         {'name': 'The Goal', 'author': 'Goldratt'},
...         {'name': 'The Source', 'author': 'Michener'},
...         {'name': 'A Walk in the Woods', 'author': 'Bryson'},
...         {'name': 'Alice in Wonderland', 'author': 'Carroll'},
...     ]
... }

它在内存中有一个地址:

>>> hex(id(outer))
'0x2299c5ee680'

我把第二本书赋值给一个变量:

>>> second_book = outer['books'][1]

如果我检查second_book的内存地址和outer中的对象,它们是相同的,这对我来说是有意义的:

>>> hex(id(outer['books'][1]))
'0x2299c5d2cc0'
>>> hex(id(second_book))
'0x2299c5d2cc0'

我现在删除对outer和garbage-collect的引用:

>>> del outer
>>> gc.collect()
0

正如预期的那样,outer不再存在:

>>> hex(id(outer))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'outer' is not defined

second_book仍然存在,并且仍然引用内存中的相同地址。我不确定会是这样,所以看到这个很有趣。

>>> second_book
{'name': 'The Source', 'author': 'Michener'}
>>> hex(id(second_book))
'0x2299c5d2cc0'

我的问题:剩余的内存(以前被整个outer dict使用)是否返回到系统,或者它仍然“锁定”,因为它的某些部分仍然被second_book引用?

5f0d552i

5f0d552i1#

这是一个具有引用计数的对象集合。当计数为零时-或者当垃圾收集器检测到只剩下循环引用时-任何给定的对象都将被删除并返回到 *python的 * 内存池。
当您创建outer时,您创建了一组对象,它们都有一个引用。outer['books'][1]只有一个引用,这是因为它所在的列表。
second_book = outer['books'][1]向该对象添加了一个引用,现在它有两个引用,但其他对象仍然只有1个引用。
del outer并没有真正删除任何东西-它减少了引用计数。外部列表开始删除(即减少引用计数)其内容。如果一个项也是一个容器,则该删除将继续向下传播。
所以每个人的裁判数都减1。除了有2个ref计数的对象之外,所有对象都将被删除,因为它们的计数将变为零。请注意,这发生在del操作期间。您不需要垃圾收集器来完成此操作。当存在循环引用时,需要GC。假设这些内部dicts也有一个引用外部dicts的键。你就有麻烦了。
一般来说,对象不会更改它们的ID/地址。如果发生这种情况,它是一个新的对象。诸如+=之类的增强操作可以创建新对象。比如说

foo = 1000
bar = foo
foo += 1

int对象的增强add创建了一个新对象1001并将其分配给foobar仍然有对1000的引用,因此不会被删除。看起来好像foo中的对象更改了地址,但实际上创建了一个新对象。

foo = [1, 2, 3]
bar = foo
foo += [4]

这里,list对象的增广加法扩展了foo中的现有列表,因此foobar仍然引用相同的内存。

相关问题