引用计数能够解决大多数垃圾回收的问题,但是遇到两个对象相互引用的情况,del语句可以减少引用次数,但是引用计数不会归0,对象也就不会被销毁,从而造成了内存泄漏问题 。针对该情况,Python引入了标记-清除机制 。
2、标记-清除标记-清除用来解决引用计数机制产生的循环引用,进而导致内存泄漏的问题。循环引用只有在容器对象才会产生,比如字典,元组,列表等 。
顾名思义,该机制在进行垃圾回收时分成了两步,分别是:
- 标记阶段,遍历所有的对象,如果是可达的(reachable),也就是还有对象引用它,那么就标记该对象为可达;
- 清除阶段,再次遍历对象,如果发现某个对象没有标记为可达(即为Unreachable),则就将其回收 。
-
>>> a=[1,2]
-
>>> b=[3,4]
-
>>> sys.getrefcount(a)
-
2
-
>>> sys.getrefcount(b)
-
2
-
>>> a.append(b)
-
>>> sys.getrefcount(b)
-
3
-
>>> b.append(a)
-
>>> sys.getrefcount(a)
-
3
-
>>> dela
>>> del b
- a引用b,b引用a,此时两个对象各自被引用了2次(去除getrefcout()的临时引用)

文章插图
- 执行del之后,对象a,b的引用次数都-1,此时各自的引用计数器都为1,陷入循环引用

文章插图
- 标记:找到其中的一端a,因为它有一个对b的引用,则将b的引用计数-1

文章插图
- 标记:再沿着引用到b,b有一个a的引用,将a的引用计数-1,此时对象a和b的引用次数全部为0,被标记为不可达(Unreachable)

文章插图
- 清除: 被标记为不可达的对象就是真正需要被释放的对象
上面描述的垃圾回收的阶段,会暂停整个应用程序,等待标记清除结束后才会恢复应用程序的运行 。为了减少应用程序暂停的时间,Python 通过“分代回收”(Generational Collection)以空间换时间的方法提高垃圾回收效率 。
3、分代回收
分代回收是基于这样的一个统计事实,对于程序,存在一定比例的内存块的生存周期比较短;而剩下的内存块,生存周期会比较长,甚至会从程序开始一直持续到程序结束 。生存期较短对象的比例通常在 80%~90%之间 。因此,简单地认为:对象存在时间越长,越可能不是垃圾,应该越少去收集 。这样在执行标记-清除算法时可以有效减小遍历的对象数,从而提高垃圾回收的速度,是一种以空间换时间的方法策略 。Python将所有的对象分为年轻代(第0代)、中年代(第1代)、老年代(第2代)三代 。所有的新建对象默认是 第0代对象 。当在第0代的gc扫描中存活下来的对象将被移至第1代,在第1代的gc扫描中存活下来的对象将被移至第2代 。
gc扫描次数(第0代>第1代>第2代)当某一代中被分配的对象与被释放的对象之差达到某一阈值时,就会触发当前一代的gc扫描 。当某一代被扫描时,比它年轻的一代也会被扫描,因此,第2代的gc扫描发生时,第0,1代的gc扫描也会发生,即为全代扫描 。
-
>>> importgc
>>> gc.get_threshold ## 分代回收机制的参数阈值设置
(700,10,10)
- 700=新分配的对象数量-释放的对象数量,第0代gc扫描被触发
- 第一个10:第0代gc扫描发生10次,则第1代的gc扫描被触发
- 第二个10:第1代的gc扫描发生10次,则第2代的gc扫描被触发
4、思考在标记-清除中,如果对象c也引用a,执行del操作后,会发生什么?对象a,b,c的引用关系如下图所示:
推荐阅读
- 《王者荣耀》连胜封号规则是怎样的?
- Linux 原来是这么管理内存的
- 中国十大名茶最新排名是怎样的?
- 中国第一个乒乓球大满贯是谁?
- 数据分析-pandas库快速了解
- VMware环境部署vFW虚拟防火墙
- 柯尔鸭子的智商有多高 柯尔鸭为什么能当宠物
- CDN的本质只是加速器?其实它才是互联网的核心精神的集大成者
- 云南大蟒蛇事件 云南挖出大蛇是真的吗
- 南极冰盖储存的水资源在地球淡水资源的 南极冰盖储存的水资源是
