python对象的内存使用实例讲解
内存管理
对象的内存使用
赋值语句:
a=1
整数1是一个对象,而a是一个引用。利用赋值语句,引用a指向对象1。python对象与引用分离!Python像使用“筷子”那样,通过引用来接触和翻动真正的食物——对象。
python内置函数id()
id()用于返回对象的内存地址!
a=1 print(id(a)) print(hex(id(a)))
可以看出对象1的内存地址为:
1428665408(十进制)
0x5527b440
当我们创建多个等于1的引用时,实际上是让所有这些引用指向同一对象!
a=1 b=1 print(id(a)) print(id(b))
输出:
1428665408
1428665408
可见a和b实际上是指向同一对象的两个引用!
is关键字
为了检验两个引用指向同一个对象,我们可以用is关键字。is用于判断两个引用所指的对象是否相同。
a=1 b=1 print(a is b) a="good" b="good" print(a is b) a="very good morning" a="very good morning" print(a is b) a=[] b=[] print(a is b)
输出:
True
True
False
False
python 能够缓存整数和短字符串,因此每个对象只存一份。
比如,所有整数1的引用都指向同一对象。即使使用赋值语句,也只是创造了新的引用,而不是对象本身。
而对于长字符串和其他对象,可以有多个相同的对象,可以使用赋值语句创建出新的对象。
引用计数getrefcount()
在Python中,每个对象都有存有指向该对象的引用总数,即引用计数(reference count)。
我们可以使用sys包中的getrefcount()。
注意:
当使用某个引用作为参数,传递给getrefcount()时,参数实际上创建了一个临时的引用。因此,getrefcount()所得到的结果,会比期望的多1。
from sys import getrefcount a=[1,2,3] print(getrefcount(a)) b=a print(getrefcount(a))
输出:
2
3
对象引用对象
我们也可以自定义一个对象,并引用其它对象
class from_obj(object): def __init__(self,to_obj): self.to_obj=to_obj b=[1,2,3] a=from_obj(b) print(id(a.to_obj)) print(id(b))
输出:
2090140984648
2090140984648
可见,a引用了对象b。
当一个对象A被另一个对象B引用时,A的引用计数将增加1。
from sys import getrefcount a=[1,2,3] print(getrefcount(a)) b=[a,a] print(getrefcount(a))
输出:
2
4
由于对象b引用了两次a,a的引用计数增加了2。
引用环
两个对象相互引用,从而构成引用环。
a=[] b=[a] a.append(b) print(getrefcount(a))
输出:
3
即对象[]被引用了2次。
即使是一个对象,只需要自己引用自己,也能构成引用环。
a=[] a.append(a) print(getrefcount(a))
输出:
3
引用环会给垃圾回收机制带来很大的麻烦。
引用减少-del关键字
a=[1,2,3] b=a print(getrefcount(b)) del a print(getrefcount(b))
输出:
3
2
如果某个引用指向对象A,当这个引用被重新定向到某个其他对象B时,对象A的引用计数减少:
a=[1,2,3] #a指向对象[1,2,3] b=a #b指向对象[1,2,3] print(getrefcount(b)) a=2 #a重新指向了2 print(getrefcount(b)) #指向对象[1,2,3]的引用减少了1
输出:
3 #指向[1,2,3]的引用为2
2 #指向[1,2,3]的引用为1
垃圾回收
当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为1。如果引用被删除,对象的引用计数为0,那么该对象就可以被垃圾回收。比如下面的表:
a = [1, 2, 3] del a
del a后,已经没有任何引用指向之前建立的[1, 2, 3]这个表。用户不可能通过任何方式接触或者动用这个对象。这个对象如果继续待在内存里,就成了不健康的脂肪。当垃圾回收启动时,Python扫描到这个引用计数为0的对象,就将它所占据的内存清空。
然而,减肥是个昂贵而费力的事情。垃圾回收时,Python不能进行其它的任务。频繁的垃圾回收将大大降低Python的工作效率。如果内存中的对象不多,就没有必要总启动垃圾回收。所以,Python只会在特定条件下,自动启动垃圾回收。当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。
我们可以通过gc模块的get_threshold()方法,查看该阈值:
import gc print(gc.get_threshold())
返回(700, 10, 10),后面的两个10是与分代回收相关的阈值,后面可以看到。700即是垃圾回收启动的阈值。可以通过gc中的set_threshold()方法重新设置。
我们也可以手动启动垃圾回收,即使用gc.collect()。