Jun*_* Gu 6 python memory memory-management
我最近遇到了关于python内存分配的这篇文章.
在这个页面中,它描述了python的内存使用情况,并且在那里有一个显示整数列表的深度复制的示例.我自己在Python 2.7上做了基准测试
Line # Mem usage Increment Line Contents
================================================
4 28.051 MiB 0.000 MiB @profile
5 def function():
6 59.098 MiB 31.047 MiB x = list(range(1000000)) # allocate a big list
7 107.273 MiB 48.176 MiB y = copy.deepcopy(x)
8 99.641 MiB -7.633 MiB del x
9 99.641 MiB 0.000 MiB return y
Run Code Online (Sandbox Code Playgroud)
所以直接删除x只删除x和所有对整数的引用到x吗?
这样做也无济于事(那么del x和del x [:]有什么区别?):
Line # Mem usage Increment Line Contents
================================================
4 28.047 MiB 0.000 MiB @profile
5 def function():
6 59.094 MiB 31.047 MiB x = list(range(1000000)) # allocate a big list
7 107.270 MiB 48.176 MiB y = copy.deepcopy(x)
8 99.637 MiB -7.633 MiB del x[:]
9 99.637 MiB 0.000 MiB return y
Run Code Online (Sandbox Code Playgroud)
与深度复制相反,如果我使用复制,删除后似乎内存恢复到新创建x时的先前状态
Line # Mem usage Increment Line Contents
================================================
4 28.039 MiB 0.000 MiB @profile
5 def function():
6 59.090 MiB 31.051 MiB x = list(range(1000000)) # allocate a big list
7 66.895 MiB 7.805 MiB y = copy.copy(x)
8 59.262 MiB -7.633 MiB del x[:]
9 59.262 MiB 0.000 MiB return y
Run Code Online (Sandbox Code Playgroud)
对于dict:
Line # Mem usage Increment Line Contents
================================================
4 28.051 MiB 0.000 MiB @profile
5 def function():
6 100.523 MiB 72.473 MiB x = dict((e, e) for e in xrange(1000000))
7 183.398 MiB 82.875 MiB y = copy.deepcopy(x)
8 135.395 MiB -48.004 MiB del x
9 135.395 MiB 0.000 MiB return y
Run Code Online (Sandbox Code Playgroud)
对于列表列表(与整数列表比较,我假设del x或del x [:]只删除堆上的那个巨大的数组列表?):
Line # Mem usage Increment Line Contents
================================================
4 28.043 MiB 0.000 MiB @profile
5 def function():
6 107.691 MiB 79.648 MiB x = [[] for _ in xrange(1000000)]
7 222.312 MiB 114.621 MiB y = copy.deepcopy(x)
8 214.680 MiB -7.633 MiB del x[:]
9 214.680 MiB 0.000 MiB return y
Run Code Online (Sandbox Code Playgroud)
所以我想问:
在这个例子中,如何在x中释放所有下划线列表?
Line # Mem usage Increment Line Contents
================================================
4 28.047 MiB 0.000 MiB @profile
5 def function():
6 248.008 MiB 219.961 MiB x = [list(range(10)) for _ in xrange(1000000)]
7 502.195 MiB 254.188 MiB y = copy.deepcopy(x)
8 494.562 MiB -7.633 MiB del x[:]
9 494.562 MiB 0.000 MiB return y
Run Code Online (Sandbox Code Playgroud)
del
不像 C 中那样释放变量,它只是表示您不再需要它。接下来发生的是实现细节。
所以这里发生的事情del
并不内存,它只是告诉 python 你已经完成了变量的处理。具体来说:
\n\n7.5。德尔声明
\ndel_stmt ::= \xe2\x80\x9cdel\xe2\x80\x9d target_list
\n删除是递归定义的,与定义赋值的方式非常相似。这里没有详细说明,而是提供一些提示。
\n删除目标列表会从左到右递归地删除每个目标。
\n删除名称会从本地或全局命名空间中删除该名称的绑定,具体取决于该名称是否出现在同一代码块的全局语句中。如果名称未绑定,则会引发 NameError 异常。
\n属性引用、订阅和切片的删除被传递给所涉及的主要对象;删除切片通常相当于分配正确类型的空切片(但即使这也是由切片对象决定的)。
\n
请注意,没有提到释放内存。相反,你告诉 python 它可以用该内存做“任何它想做的事”。在这种情况下,您的 python 实现(我假设是 CPython)将内存存储在内存缓存中以供以后使用。这使得 python 运行得更快,因为以后不需要分配那么多内存。
\n考虑这个例子,我们del x
然后再次创建一个副本y
。请注意,第二次复制期间分配的内存量小于第一次复制期间分配的内存量。这是因为内存被重复使用。如果我们再次这样做,我们会发现在第三次复制期间几乎没有分配任何内存,因为 python 只是重新使用之前分配的内存:
Line # Mem usage Increment Line Contents\n================================================\n 4 34.777 MiB 0.000 MiB @profile\n 5 def function():\n 6 37.504 MiB 2.727 MiB x = [list(range(10)) for _ in xrange(10000)]\n 7 40.773 MiB 3.270 MiB y = copy.deepcopy(x)\n 8 40.773 MiB 0.000 MiB del x\n 9 41.820 MiB 1.047 MiB y2 = copy.deepcopy(y)\n 10 41.820 MiB 0.000 MiB del y2\n 11 41.824 MiB 0.004 MiB y3 = copy.deepcopy(y)\n 12 41.824 MiB 0.000 MiB return y\n
Run Code Online (Sandbox Code Playgroud)\n优秀的“博客”:http://www.evanjones.ca/memoryallocator/
\nhttp://effbot.org/pyfaq/why-doesnt-python-release-the-memory-when-i-delete-a-large-object.htm
\n