“del”到底做了什么?

时间:2014-01-10 20:00:33

标签: python memory python-internals

这是我的代码:

from memory_profiler import profile

@profile
def mess_with_memory():
    huge_list = range(20000000)
    del huge_list
    print "why this kolaveri di?"

当我从解释器运行它时,这就是输出:

Line#Mem使用量增加行内容

 3      7.0 MiB      0.0 MiB   @profile
 4                             def mess_with_memory():
 5                             
 6    628.5 MiB    621.5 MiB       huge_list = range(20000000)
 7    476.0 MiB   -152.6 MiB       del huge_list
 8    476.0 MiB      0.0 MiB       print "why this kolaveri di"

如果您注意到输出,创建巨大的列表消耗621.5 MB,而删除它只释放152.6 MB。当我查看docs时,我发现了以下声明:

the statement del x removes the binding of x from the namespace referenced by the local scope

所以我猜,它没有删除对象本身,只是取消绑定它。 但是,在取消绑定时它做了多少空间(152.6 MB)。有人可以请痛苦地向我解释这里发生了什么吗?

1 个答案:

答案 0 :(得分:29)

Python是一种垃圾收集语言。如果您的代码中的某个值不再“可访问”,则最终会被删除。

如您所见,del语句删除了变量的绑定。变量不是值,它们只是值的名称。

如果该变量是对任何地方的值的唯一引用,则该值最终将被删除。特别是在CPython中,垃圾收集器建立在引用计数之上。因此,“最终”意味着“立即”。*在其他实施中,它通常“很快”。

但是,如果存在对同一值的其他引用,则只需删除其中一个引用(无论是del xx = None,还是退出x存在的范围等。没有清理任何东西。**


这里还有另一个问题。我不知道memory_profiler模块(大概是this one)实际测量的是什么,但描述(谈论psutil的使用)听起来就像是从“外部”测量你的内存使用情况。

当Python释放存储时,它并不总是 - 甚至通常 - 将其返回给操作系统。它在多个级别保持“自由列表”,因此它可以更快地重新使用内存,而不是必须一直回到操作系统要求更多。在现代系统中,这很少是一个问题 - 如果您再次需要存储,那么您拥有它是件好事;如果你不这样做,它会在其他人需要时立即被分页,并且永远不会被重新分页,所以没有什么害处。

(最重要的是,上面我称之为“操作系统”实际上是一个由多个级别组成的抽象,从malloc库到核心C库到内核/寻呼机,以及这些级别中至少有一个通常有自己的免费列表。)

如果你想从内部角度追踪内存使用......那么,这很难。由于新的tracemalloc模块,它在Python 3.4中变得更加容易。有各种第三方模块(例如,heapy / guppyPymplermeliae)尝试使用早期版本获取相同类型的信息,但这很困难,因为在PEP 445之前获取来自各种分配器的信息并将这些信息绑定到垃圾收集器是非常困难的。


*在某些情况下,对值的引用...但仅限于其他本身无法访问的引用,可能在一个循环中。就垃圾收集器而言,这仍然被视为“无法访问”,但就引用计数而言并非如此。因此,CPython还有一个“循环检测器”,每隔一段时间运行一次,并找到可以互相访问的循环,但不能从任何其他人那里获取并清理它们。

**如果您在交互式控制台中进行测试,可能会隐藏对您的值的引用,这些引用难以跟踪,因此您可能认为您已经摆脱了最后的引用你还没有。在脚本中,它应始终可能,如果不是 easy ,则要解决问题。 gc模块可以提供帮助,调试器也可以提供帮助。但当然,他们两个为您提供了添加其他隐藏参考的新方法。