您的位置:首页 > 编程语言 > Python开发

Python的垃圾回收机制

2018-02-07 17:12 260 查看
作者:Wilson_Iceman 出处:http://blog.csdn.net/Wilson_Iceman 欢迎转载, 但请保留这段声明。多谢!

今天我们来谈谈Python中的垃圾回收机制,每一种语言都有自己的垃圾回收机制,从C语言的手动回收,到java,js和python的自动回收,每一种回收机制都有自己的特点,今天我们来谈谈python的垃圾回收机制,看看它是怎么工作的。

关于引用

第一个问题是有关引用的,其实在我之前的文章可变与不可变类型中已经谈到了这个问题,我们再回忆一下。先看最简单的例子,赋值>>>a = 1
整数1是一个对象(Python中一切皆对象),而a是一个引用,通过赋值语句,将a指向了对象1。在Python中,对象与引用的分开的,这样做的好处是可以节省内存。在Python中,每个对象都存在一个存有指向该对象的引用总数,即引用计数。我们可以通过系统自带的getrefcount来查看该对象的引用计数。

from sys import getrefcount
import gc

a = [1, 2, 3]
print(getrefcount(a))
b = a
print(getrefcount(b))
print(getrefcount(a))
a = 1
print(getrefcount(b))
上面的运行结果是
>>>2
>>>3
>>>3
>>>2
注意在运行getrefcount的时候,对自动创建一个对象的临时引用,因此会再加1。a引用了一次,加1就是2。b有引用了a,因此b应用了2次,再加1是3,。而此时a和b引用同一个对象,因此a也是3。a再被赋值1,此时b就少了一次引用,因此b变成1,再加1就是2。
通过上面的例子,我们可以看到对象的引用计数可能增加,也可能减少。我们简单总结一下:
引用计数增加的情况:

对象被创建。a = 1
对象被引用。b = a
对象被作为参数传入给函数。do_sth(a)
作为容器对象的元素。mylist = [a, b, c]
引用计数减少的情况:

作用域丢失。比如上面的函数do_sth(a)结束,则a引用计数减1
对象被显式销毁。del a
对象的别名被赋值给其他对象。a = 1,之后a = 2,则之前的对象1的引用会减1
从一个容器对象中剔除。mylist.remove(a)
容器对象本身被销毁。del mylist
关于回收机制

了解了上面的内容,我们就可以很容易的理解Python的回收机制,即当该对象的引用计数为0时,该对象就会被回收。这个其实很好理解,对于一个引用计数为0的对象,说明它的引用已经没有了,因此也没有存在的必要了。Python就不时的扫描文件中所有对象的引用计数,当一个对象的引用计数为0时,Python就会把它回收,同时释放掉它所在的内存。

但是这样存在一个问题,就是当程序中存在大量的对象时,那么检查对象的引用计数将会变成一件非常耗时耗资源的事情,但是我们的python非常聪明,它有自己的办法来解决这个问题,即分代回收

关于分代回收

简单的说就是存活越久的对象,就越不可能成为垃圾,自然就越不会被回收,那既然是这样,那么存活越久的对象,应该越不会
被扫描到。Python其实就是这么干的,它把对象分成0代对象,1代对象和2代对象。对象在被建立时都是0代对象,当经历过Python
几次扫描后依然存在,那么它就会“竞升”为1代对象,以此类推“竞升”为2代对象。Python对不同代对象,扫描的频率是不一样的,我
们可以通过get_threshold()来检查一下。
import gc

print(gc.get_threshold())
上面的程序运行后会返回(700,10,10),有三个值,第一个值700是一个阈值,它表示Python启动垃圾回收的条件,即,产生对象
和销毁对象的次数超过这个阈值时,Python才会启动垃圾回收机制。后面两个值10分别表示,启动扫描10次0代对象,才会扫描1次1代
对象,扫描10次1代对象,才会扫描1次2代对象。当然我们也可以通过set_threshold()函数来修改这3个参数。
以上就是今天的全部内容。
与您共勉!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息