垃圾回收机制一般有两个阶段:垃圾检测和垃圾回收。
Python GC 主要使用引用计数来跟踪和垃圾回收。在引用计数的基础上,通过“标记-清除”解决容器对象可能产生的循环引用问题,通过“分代回收”以空间换时间的方法来提高垃圾回收效率。
Python 的垃圾回收经历了三个阶段:
- 引用计数
每个对象维护一个引用计数,用来记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数会 +1,当对象的引用失效时,引用计数 -1。一旦引用计数为 0,那么对象立即被回收,对象占用的内存空间会被释放。但是它无法解决对象的“循环引用”。我估计 C++ 程序员对这块应该特别理解。
Python 通过sys
模块来查看对象的引用计数
>>> import sys
>>> a = [1, 2]
>>> sys.getrefcount(a)
2
# 为什么会是 2 呢?因为传入函数时也使用的是引用
# 但是这里还有一个很令我迷惑的问题
# 当引用计数一个 数字的时候,这个值很大
>>> sys.getrefcount(2)
931 --> 你机器上可能会不同
# 难道是因为 2 在 Python 中被缓存了??
有几种方式可以增加引用计数:
对象被创建 , 别名被创建
传递给函数, 成为容器对象的一个元素
减少引用计数方式:
对象/别名被销毁, 离开函数作用域
- 标记清除
标记清除(Mark—Sweep)算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。等到没有空闲内存的时候,从寄存器和栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象上打标记,然后清扫内存,没有标记的对象释放。 - 分代回收
分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。同时,分代回收是建立在标记清除技术基础之上。
来自于网上:
Python 实现了一个内存池机制,将不同的内存放到操作系统而不是返回给操作系统。Python 中所有小于 256 字节的对象都使用 pymalloc 实现的分配器,大对象使用系统的 malloc。
网友评论