背景介绍
Python语言默认采用的垃圾收集机制是『引用计数法 Reference Counting』,该算法最早George E. Collins在1960的时候首次提出,50年后的今天,该算法依然被很多编程语言使用
引用计数法是什么?
引用计数法是一种垃圾收集机制,而且也是一种最直观,最简单的垃圾收集技术,。
当一个对象的引用被创建或者复制时,对象的引用计数加 1;当一个对象的引用被销毁时,对象的引用计数减 1;当对象的引用计数减少为 0 时,就意味着对象已经没有被任何人使用了,可以将其所占用的内存释放了。
优点:
简单、实时性:一旦没有引用(引用计数为0),内存就直接释放了。不会像其他机制等到特定时机。
实时性还带来一个好处:处理回收内存的时间分摊到了平时。
引用计数有哪些缺点
1、引用计数机制执行效率问题:
引用计数机制所带来的维护引用计数的额外操作与 Python 运行中所进行的内存分配和释放,引用赋值的次数是成正比的。而这点相比其他主流的垃圾回收机制,比如“标记-清除”,“停止-复制”,是一个弱点,因为这些技术所带来的额外操作基本上只是与待回收的内存数量有关。
2、致命弱点:循环引用(也称交叉引用)
循环引用可以使一组对象的引用计数不为 0,然而这些对象实际上并没有被任何外部对象所引用,
它们之间只是相互引用。这意味着不会再有人使用这组对象,应该回收这组对象所占用的内存空间,然
后由于相互引用的存在,每一个对象的引用计数都不为 0,因此这些对象所占用的内存永远不会被释放。
这一点是致命的,这与手动进行内存管理所产生的内存泄露毫无区别。
怎么解决这种问题呢?
标记-清除:“标记-清除”是为了解决循环引用的问题。可以包含其他对象引用的容器对象(比如:list,set,dict,class,instance)都可能产生循环引用。原理:标记删除第一步:对执行删除(-1)后的每个引用-1,那么a的引用就是0,b的引用为1,将a放到死亡容器,将b放到存活容器。
标记删除第二步:循环存活容器,发现b引用a,复活a:将a放到存活容器内。
标记删除第三步:删除死亡容器内的所有对象。
分代回收 :“标记-清除”所带来的额外操作实际上与系统
中总的内存块的数量是相关的,内存块越多,垃圾检测操作就越多。为了提高垃圾收集的效率,采用“空间换时间的策略”。原理:将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。
解决了这么多的问题,最终目的还是为了节省内存空间,那除了回收机制,我们还能从哪些地方节省内存空间以及优化速度呢?
- 小整数对象池
整数在Python中的使用广泛,为了优化速度,使用整数对象池来优化,整数对象池中的数据不会被垃圾回收。
范围 :[-5,256]
单个字母也是同样原理
- intern机制
当重复创建若干变量,变量内容相同时,Python不会为相同的内容创建存储空间,
而是使用引用计数的方式处理。
什么情况下不共用内存?
字符串数据如果存在空格则不共用数据
data = ‘love python’
data2 = ‘love python’
id(data) 不等于 id(data2)
如果是:
data0 = ‘love_python’ data1 = ‘love_python’
则:是指向同一块内存空间
不可变类型进行修改,实际是创建了新的对象,指向了另一个内存空间。
拓展浅谈其他语言的回收机制
c语言的垃圾回收是人工的,工作量大,但是可控性高。
java是自动化的,但是可控性很差,甚至有时会出现内存溢出的情况。