引用计数
1.当一个对象的引用被创建或者复制时,对象的引用计数加1
2.当一个对象的引用被销毁时,对象的引用计数减1
3.当对象的引用计数减少为0时,该对象没有被任何人所引用,因此这时候可以释放该对象所占的内存资源
这种机制有一个很大的特点:虽然可以实时的回收内存,但是实时的内存回收会消耗额外的资源,进而影响程序的执行效率,还有一个缺点就是当某些对象引用未释放时,该资源是无法被回收的

a = 1
b = a



这时1的引用计数是2,可以通过sys.getrefcount(a)来查看引用的数量,使用sys.getrefcount()的时候会临时创建一个引用,所以在获取引用数量的时候会 +1,


有些说通过sys.getrefcount(obj) 来获取对象的引用计数,并根据返回值是否为 0 判断是否可以被垃圾回收器回收,但是sys.getrefcount()是不可能返回0的。

sys.getrefcount(a)
3



对与交叉引用,如果使用引用计数来处理,两者之间互相引用导致引用计数不可能为零,导致不能回收

a = []
b = []
a.append(b)
b.append(a)
print a,b
输出:
[[[...]]] [[[...]]]



标记-清除


对与交叉引用可以使用标记-清除的方式对内存进行回收,


算法依赖于对所有存活对象进行一次全局遍历来确定哪些对象可以回收,


1.遍历全局存活对象,找出所有的根集合(根的概念还不是太肯定,确定之后再补)


2.从每一个根出发,找到所有可达对象(真实的对象本身,而非引用),除此之外,其它不可达的对象就是垃圾对象,可被回收。


整个过程分为两个阶段:标记阶段找到所有存活对象;清除阶段清除所有垃圾对象。




相比较引用计数算法,标记-清除算法可以非常自然的处理环形引用问题,另外在创建对象和销毁对象时时少了操作引用计数值的开销。


注意:在清除时应用程序必须暂时停止




分代回收


频繁的标记清除会占用很多资源,分代回收会根据内存块存在的时间,降低对存活时间长的内存块的标记清理频率,从而降低资源开销


1.将系统中的所有内存块根据其存活时间划分为不同的集合


2.每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。


3.存活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。


通常是利用几次垃圾收集动作来衡量集合的存活时间,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。



分代回收是以空间换取时间来提高垃圾回收的效率


小结

Python的GC模块主要运用了“引用计数”(reference counting)来跟踪和回收垃圾。在引用计数的基础上,
还可以通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用的问题。
通过“分代回收”(generation collection)以空间换取时间来进一步提高垃圾回收的效率。