理解Python内存分配并释放

6

我最近看到了这篇关于Python内存分配的文章:http://deeplearning.net/software/theano/tutorial/python-memory-management.html

在这个页面上,它描述了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

直接删除 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

相对于深拷贝而言,如果我使用浅拷贝,在删除后,似乎当新创建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

对于字典:

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

对于列表的列表(与整数列表相比,我假设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

所以我想问:

  1. 如果没有方法可以回收整数占用的内存,整数也是一个对象,为什么内存完全不会被释放?只是整数无法被释放吗?或者浮点数和字符串也一样?对象引用也是如此吗?
  2. 为什么内存中有-7 MB?这是因为作为数组列表实现的列表已经从堆中释放了吗?
  3. 无论是列表还是字典,del 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
1个回答

1

简述

del 语句并不像 C 语言一样释放变量内存,它只是表示你不再需要该变量。接下来所发生的事情是实现细节。

原理

这里发生的是 del 并没有释放内存,它只是告诉 Python 你已经使用完该变量了。具体地说:

7.5. del语句
del_stmt ::= “del” target_list
删除与赋值类似,具有递归定义。以下是一些提示。
递归删除目标列表中的每个目标,从左到右。
删除名称会将该名称的绑定从本地或全局命名空间中删除,具体取决于该名称是否出现在同一代码块中的全局语句中。如果名称未绑定,则会引发NameError异常。
属性引用、订阅和切片的删除传递给涉及的主对象;删除切片通常等效于对正确类型的空切片进行赋值(但甚至这也由被切对象确定)。
请注意,没有提及释放内存。相反,您告诉Python可以使用该内存“做任何它想做的事情”。在这种情况下,您的Python实现(我假设是CPython)将该内存存储在内存缓存中以供稍后使用。这样做可以使Python运行得更快,因为不需要稍后分配太多内存。
示例
考虑这个例子,我们先del x然后再次创建y的副本。请注意,在第二次复制期间分配的内存量比第一次少。这是因为重新使用了内存。如果我们再做一遍,我们会发现在第三次复制期间几乎没有分配任何内存,因为Python只是重新使用以前分配的内存:
Line #    Mem usage    Increment   Line Contents
================================================
     4   34.777 MiB    0.000 MiB   @profile
     5                             def function():
     6   37.504 MiB    2.727 MiB       x = [list(range(10)) for _ in xrange(10000)]
     7   40.773 MiB    3.270 MiB       y = copy.deepcopy(x)
     8   40.773 MiB    0.000 MiB       del x
     9   41.820 MiB    1.047 MiB       y2 = copy.deepcopy(y)
    10   41.820 MiB    0.000 MiB       del y2
    11   41.824 MiB    0.004 MiB       y3 = copy.deepcopy(y)
    12   41.824 MiB    0.000 MiB       return y

来源

优秀的“博客”:http://www.evanjones.ca/memoryallocator/

http://effbot.org/pyfaq/why-doesnt-python-release-the-memory-when-i-delete-a-large-object.htm


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接