本文将详细地介绍python内存管理的细节,虽然我们写代码的时候不用去关心复杂的内存管理任务,但是了解python内存管理的流程,能够使我们对数据存储和引用更加得心应手。

内存分配

之前也说过,在python中变量名和类型都无需事先申明(我爱python理由之一),这些都是在赋值的时候决定的。Python解释器承担了内存管理的复杂任务,我们只要负责编写代码即可。我们新创建一个对象,并对这个对象进行各种操作的时候,Python解释器是如何进行内存管理的呢?在这里就要介绍下python内部的引用计数器了。

引用计数器

要保持追踪内存中创建的对象,Python使用了引用计数器这种技术。顾名思义,就是记录所有使用中的对象各有多少引用次数。当对象被创建时,它的引用次数为1,而当它的引用次数变成0时,该对象就会被垃圾回收机制处理,从而释放出内存。那么增加和减少引用计数的方式有哪些呢?

增加引用计数的方式:

对象被创建:

x = 1

1

x=1

此时变量名x指向对象1,对象1的引用计数为1,如图所示:

Python内存冲突 python 内存机制_python

别名被创建:

y = x

1

y=x

此时变量名y也指向对象1,对象1的引用计数为2,如图所示:

Python内存冲突 python 内存机制_python_02

被作为参数传递给函数:

funtion(x)

1

funtion(x)

成为容器对象(列表,元组,字典)中的元素:

list1 = [a, b, x]

1

list1=[a,b,x]

减少引用计数的方式:

变量名指向另一个对象:

x = 1

y = x

y = 2

1

2

3

x=1

y=x

y=2

本来变量名x、y都指向对象1,引用次数为2,当把对象2赋值给变量名y时,变量名y不再指向对象1了,此时引用次数变为1,如图所示:

Python内存冲突 python 内存机制_变量名_03

函数结束时:

比如function(x)结束后,对象1的引用次数减少1次。

显式删除别名:

del y
1
dely
从容器对象中移除:
list1.remove(x)
1
list1.remove(x)
或者容器对象被删除:
del list1
1
dellist1

这些方式都会使对象的引用计数减少。

垃圾收集器

垃圾收集器负责释放内存,它会寻找引用计数为0的对象,也会检查那些引用计数大于0但也应该被销毁的对象,比如循环引用。

一个典型的循环引用如下:

#moduleA
from moduleB import b
def a():
print 'aaa'
b()
def c():
print 'ccc'
if __name__ == '__main__':
a()
#moduleB
from moduleA import c
def b():
print 'bbb'
c()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#moduleA
frommoduleBimportb
defa():
print'aaa'
b()
defc():
print'ccc'
if__name__=='__main__':
a()
#moduleB
frommoduleAimportc
defb():
print'bbb'
c()

有两个模块moduleA 和 moduleB,首先在A中导入B,进入B中,发现B中又导入了A又回到A中,但是A又导入B这就形成了循环引用。一个循环引用发生在至少两个对象互相引用时,也就是说所有的引用都消失时,这些引用仍然存在,这说明单靠引用计数还不够。

因此Python的垃圾收集器实际上是由引用计数器和循环垃圾收集器组成。当一个对象的引用计数为0时,解释器暂停,释放掉这个对象;同时垃圾收集器会检查未通过计数销毁的对象,在这种情况下,解释器暂停,试图清理所有未引用的循环。

Python对象

再次重申,python中一切皆对象。python使用对象来存储数据,所以python是一门面向对象的编程语言。python对象包括三个特性:

身份:身份表示对象的内存地址,可以通过内建函数id()查看。

类型:对象的类型决定了保存什么样的数据,可以进行什么样的操作,可以通过内建函数type()查看。

值:对象包含的数据。

这三个特性在对象被创建的时候就自动产生。

指向相同的对象:

x = y = 1
1
x=y=1
以及:
x = 1
y = x
1
2
x=1
y=x

这两种情况都是将变量名x、y指向对象1。除此之外对于不可变对象(比如:整型对象和字符串对象),不同变量名也会指向相同的对象。

x = 1
y = 1
1
2
x=1
y=1

在这里我们认为Python应该会创建两个对象,事实上变量名x、y还是指向了同一个对象1,我们可以通过内建函数id()来查看。

>>>id(x)
1710132261416
>>>id(y)
1710132261416
1
2
3
4
>>>id(x)
1710132261416
>>>id(y)
1710132261416

两者的内存地址一样。这是因为python认为这些不可变对象会经常被用到。

指向不相同的对象:

x = 1.0
y = 0.5 + 0.5
1
2
x=1.0
y=0.5+0.5
以及:
x = 1.0
y = 1.0
1
2
x=1.0
y=1.0

Python内存冲突 python 内存机制_Python内存冲突_04

虽然对象的值是一样的,但是对象的内存地址不同,仍是两个不同的对象,可以通过内建函数id()来验证:

>>>id(x)
1710172008712
>>>id(y)
1710172008784
1
2
3
4
>>>id(x)
1710172008712
>>>id(y)
1710172008784