理解python内存分配并释放

时间:2017-08-15 06:45:22

标签: python memory memory-management

我最近遇到了关于python内存分配的this article

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

对于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

对于列表列表(与整数列表比较,我假设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只能释放数据结构本身(我的意思是数组列表结构,或dict结构),但整数,对象引用可以标记为空闲,但不能回到系统?
  4. 在这个例子中,我如何或者如何在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 个答案:

答案 0 :(得分:0)

TL; DR

del不像C中那样释放变量,只是说你不再需要它。然后发生的是实现细节。

Rationalle

所以这里发生的事情是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