浪费空间或内存泄漏:Python

时间:2013-11-05 19:54:10

标签: python

我对以下情况感到困惑,也许我的词汇在这里是错误的,所以道歉。

假设我们有一个元组x = ('a', []),然后我们会x = (x[0], [1, 2, 3])

由于新元组引用了旧元组,我们无法删除旧的元组对象,但由于旧的元组对象,我们只使用对x [0]的引用,因此我们将内存浪费为x [1]任何东西都无法访问旧元组对象。

这是内存泄漏的真实案例吗?我们是在浪费内存,以便新的元组对象的引用有意义。

3 个答案:

答案 0 :(得分:5)

你可能从像C ++这样的语言来到Python,其中变量是存储值的内存位置。在Python中,值存储在你不必担心的地方,你的变量只是名字引用这些值。没有办法让变量引用另一个变量* - 你可以让它引用相同的作为另一个变量,但这不是问题。


例如,在C ++中:

int x[] = {1, 2, 3};
int &y = x[0];

此处,x是一个足以容纳三个int值的内存位置,y是对这些位置中的第一个的引用。因此,如果xy仍然存在的情况下消失,那么您将有一个悬空参考,这将是不好的。但在Python中:

x = [1, 2, 3]
y = x[0]

此处,xlist对象的名称,其三个位置是存储在其他地方的三个不同int对象的名称,y只是另一个名称第一个int对象。如果x消失,Python可以释放list,之后它可以释放23个对象**(因为没有其他人引用它们),只留下1对象。


这也是Python中没有“复制构造函数”***的原因。在C ++中,int z = x[0]创建一个新的内存位置,并隐式地将intx[0]复制到该内存位置;在Python中,除非你明确写出类似z = copy.copy(x[0])之类的内容,否则你永远不会复制任何内容。


*如果你看看封闭细胞是如何在封面下工作的,那就不是真的了。

**事实上,小整数通常是专门处理的,并且永远保持活着,但让我们忽略它。

***更少的拷贝赋值运算符,移动构造函数和移动赋值运算符。

答案 1 :(得分:4)

这是一个带注释的Python会话,使用您的示例来显示当前引用的内容。

>>> x = ('a', [])
>>> id(x)
4340112344            # The tuple
>>> id(x[0])
4339406128            # The character 'a'
>>> id(x[1])
4340109184            # An empty list
>>> x = (x[0], [1,2,3])
>>> id(x)
4340112488            # x refers to a new object
>>> id(x[0])
4339406128            # The character 'a'. The old tuple is not involved
>>> id(x[1])
4340199576            # A new list.

原始元组(对象4340112344)不再被引用,因此可以随时方便地进行垃圾回收,而不会影响x引用的新对象。

答案 2 :(得分:-2)

轻微的逻辑错误。

Python字符串是不可变的。执行此操作时:

x = ('a', [])

然后这个:

x = (x[0], [1,2,3])

x[0]现在是一个新字符串,即与原始字符串无关。因此,GC可以自由收集原始元组。

以上是无效的!!! Acck !!!!!!! MISSHAPED LOGIC !!!较低的文字对所有类型都有效!!!

然而,如果我们这样做:

x = (x[1], 'Hi!')

即使x[1]是对列表的引用,原始元组仍会被删除。为什么?因为列表与元组无关。这两个是独立的对象。因此,当您引用x[1]时,可以安全地删除其他元素以及原始元组本身。

底线:如果一个物体还活着,那并不意味着它的父物是活着的。如果我们继续覆盖x,则每次都会删除它,除了需要保持活动的一个对象。

所以,没有内存泄漏。