python的垃圾回收机制

Python不像C++,Java等语言一样,他们可以不用事先声明变量类型而直接对变量进行赋值。对Python语言来讲,对象的类型和内存都是在运行时确定的。这也是为什么我们称Python语言为动态类型的原因

这里我们把动态类型可以简单的归结为对变量内存地址的分配是在运行时自动判断变量类型并对变量进行赋值

现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式。自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患。
对于一个字符串、列表、类甚至数值都是对象,且定位简单易用的语言,自然不会让用户去处理如何分配回收内存的问题。
python里也同java一样采用了垃圾收集机制,不过不一样的是:
python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略

Python的GC模块主要运用了引用计数来跟踪和回收垃圾

  • 在引用计数的基础上,还可以通过“标记-清除”解决容器对象可能产生的循环引用的问题。
  • 通过分代回收以空间换取时间进一步提高垃圾回收的效率。

引用计数机制

引用计数的缺陷是循环引用的问题
为每个内存对象维护一个引用计数.
当有新的引用指向某对象时就该该对象的引用计数加1,当指向该对象的引用被销毁时将该对象计数减1,当计数归零时,就回收该对象所占用的内存资源

标记-清除

分两个步骤
1、标记:即从众多的内存对象中区分出不在会被使用的垃圾对象;
2、清除:把标记的垃圾对象清除.标记的时候需要确定内存对象的集合Root set,集合里的对象都是可以访问的.如果root set中的对象引用了其他的对象,那么被引用的对象也不能被标记为垃圾对象.然后从root set出发,递归遍历root set能访问到的所有对象,进行标记为不是垃圾对象.遍历结束后,没有被标记的就是垃圾对象

分代收集

根据一个统计学上的结论,如果一个内存对象在某次Mark过程中发现不是垃圾,那么它短期内成为垃圾的可能性就很小。分代收集将那些在多次垃圾收集过程中都没有被标记为垃圾对象的内存对象集中到另外一个区域——年老的区域,即这个区域中的内存对象年龄比较大。因为年老区域内内存对象短期内变成垃圾的概率很低,所以这些区域的垃圾收集频率可以降低,相对的,对年轻区域内的对象进行高频率的垃圾收集。这样可以提高垃圾收集的整体性能。

python中的内存管理

在CPython中,大多数对象的生命周期都是通过对象的引用计数来管理的。引用计数是一种最直观、最简单的垃圾收集计数,与其他主流GC算法比较,它的最大优点是实时性,即任何内存,一旦没有指向它的引用,就会立即被回收。

然而引用计数存在两个比较麻烦的缺陷:

当程序中出现循环引用时,引用计数无法检测出来,被循环引用的内存对象就成了无法回收的内存,引起内存泄露。比如

list1 = []
list1.append(list1)
del list1

list1循环引用了自身,第二行执行完后,list1的GC变成了2,执行完del操作后,list1的引用计数变为1,并没有归零,list1的内存空间并没有被释放,造成内存泄露。
维护引用计数需要额外的操作。

在每次内存对象呗引用或者引用被销毁时都需要修改引用计数,这类操作被称为footprint。引用计数的footprint是很高的,使得程序的整体性能受到很大的影响。

为了解决循环引用的问题,CPython特别设计了一个模块——GC module,其主要作用就是检查出循环引用的垃圾对象,并清除他们。该模块的实现,实际上也是引入了前面提到的两种主流的垃圾收集技术——标记清除和分代收集。

Python为了解决引用计数的性能问题,尽量再内存的分配和释放上获得最高的效率,还设计了大量的内存池机制。

你可能感兴趣的:(python的垃圾回收机制)