"del"究竟做了什么?

Man*_*ain 22 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?"
Run Code Online (Sandbox Code Playgroud)

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

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"
Run Code Online (Sandbox Code Playgroud)

如果你注意到输出,创建巨大的列表消耗621.5 MB,而删除它只需释放152.6 MB.当我检查文档时,我发现了以下声明:

the statement del x removes the binding of x from the namespace referenced by the local scope
Run Code Online (Sandbox Code Playgroud)

所以我猜,它并没有删除对象本身,而只是取消绑定它.但是,它在解除绑定方面做了什么呢?它释放了如此多的空间(152.6 MB).有人可以请痛苦地向我解释这里发生了什么吗?

aba*_*ert 31

Python是一种垃圾收集语言.如果某个值不再可以从您的代码"到达",它最终会被删除.

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

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

如果有相同的值其它参考,但是,仅仅取消这些参考之一(无论是del x,x = None在离开这里的范围x存在,等)不干净东西了.**


这里还有另一个问题.我不知道memory_profiler模块(可能是这个模块)实际测量的是什么,但描述(谈论使用psutil)听起来像是从"外部"测量你的内存使用量.

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

(最重要的是,上面我称之为"操作系统"实际上是一个由多个级别组成的抽象,从malloc库到核心C库再到内核/寻呼机,并且这些级别中至少有一个通常具有自己的免费清单.)

如果你想从内部角度追踪内存使用......那么,这很难.由于新tracemalloc模块,它在Python 3.4中变得更加容易.有各种第三方的模块(例如,heapy/ guppy,Pympler,meliae),尝试得到同样的与早期版本的信息,但是很难,因为正从各个分配器信息,系该信息的垃圾收集器,非常在PEP 445之前很难.


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

**如果您在交互式控制台中进行测试,可能会隐藏对您的值的引用,这些引用难以跟踪,因此您可能会认为在没有的情况下已经删除了最后一个引用.在脚本中,它应该永远是可能的,如果不容易,要理出头绪.该gc模块可以提供帮助,调试器也可以提供帮助.不过,当然他们两个给你新的方法来增加额外的隐藏引用.

  • @ user2864740:没错.这记录不起作用; 事实上它有时会工作,有时甚至是段错误,有时会在半小时之后导致段错误,因为你损坏了某些东西,有时只是默默地给你错误的值,如果你真的伸展定义那么只算作"工作"...... (5认同)
  • 不能达到?参考?您可以随时尝试*通过 id 恢复流放的变量!http://stackoverflow.com/a/15012814/194586(*可能会也可能不会导致段错误) (3认同)
  • @NickT这些变量不是"死"..在任何情况下,"可能导致段错误"的事实意味着对象*可以*被回收(因此*无法通过GC根目录*;该帖子仅显示可以通过*opaque标识符*获取对象(如果它们仍然存在). (2认同)
  • @AshwiniChaudhary:在2.x中,除了[编写C扩展相关的小位](http://docs.python.org/2/c-api/memory.html)之外,它只在源代码中记录,主要在[`obmalloc.c`](http://hg.python.org/cpython/file/2.7/Objects/obmalloc.c)中. (2认同)
  • @abarnert:谢谢。gc 模块确实有帮助。做 gc.collect() 释放了剩余的内存:) (2认同)