Gc简介

Gc(garbage collection)中文直译为垃圾回收,是一种回收内存空间避免内存泄漏的机制。

我们在程序的运行中会产生大量的对象用于保存数据,而有时候有些对象已经没有用了就需要被清理释放掉该对象所占据的内存空间。

在一些较为低级的语言中对于内存空间的释放是需要编程人员来手动进行的,这种与底层硬件直接打交道的操作是十分的危险与繁琐的,而基于C语言开发而来的Python为了解决掉这种顾虑则自带了一种垃圾回收机制,从而让开发人员不必过分担心内存的使用情况而可以全身心的投入到开发中去。

引用计数

最简单的Gc机制,引用计数。

首先将堆区内存中的对象与栈区内存中标识符的绑定数量做一个计数。

示例如下:

>>> a = "Python"
>>> b = "Python"
>>> c = "Python"

我们的图示按照标识符的引用次数为准,并忽略临时引用,下图中Python这个str对象的引用计数目前为3:

cpython内存管理 python内存管理简称_引用计数

每次标识符与对象取消绑定关系,则计数-1,当计数减到0的时候将自动清理该对象。

示例如下:

>>> del a
>>> del b
>>> del c

cpython内存管理 python内存管理简称_引用计数_02

标记清除

循环引用

引用计数能够解决百分之九十的问题,但是有一种特殊的情况是引用计数处理不了的,即循环引用(也被称为交叉引用)。

什么是循环引用,举一个简单的例子就是列表的互相嵌套,如下所示,l1和l2的引用计数都为2(标识符+1次引用,两个列表的[-1]索引处+1次引用):

>>> l1 = [1,2,3]
>>> l2 = [1,2,3,l1]
>>> l1.append(l2)
>>> l1
[1, 2, 3, [1, 2, 3, [...]]]
>>> l2
[1, 2, 3, [1, 2, 3, [...]]]

cpython内存管理 python内存管理简称_Python_03

接着往下看,我们取消标识符与对象的绑定关系:

>>> del l1
>>> del l2

现在,由于2个列表对象的计数都为1,故引用计数的策略显得不好使了。

因为2个列表对象的引用计数都未清0:

cpython内存管理 python内存管理简称_cpython内存管理_04

解决方案

为了解决循环引用带来的内存泄露问题,出现了标记清除法。

标记清除的意思在于当应用程序可用内存空间即将被耗尽时便开始遍历栈区所有的标识符,并且会顺着栈区标识符对其引用的在堆区中的对象做一个标记。

如果堆区内存中存在没有与栈区标识符进行绑定的对象,该对象则会认为是无用的对象,将会被清理。

cpython内存管理 python内存管理简称_cpython内存管理_05

分代回收

基于引用计数的垃圾回收机制每一次执行清理操作前都会将整个堆区对象的引用计数做一次遍历统计。

这样做是非常消耗时间的,所以Python垃圾回收机制为了效率的提升加入了分代回收的策略。

即:

  • 当多次扫描后,若该对象的引用计数一直不为0,且也没有被标记清除法所清理掉,则证明
    该对象会被经常使用,因此降低该对象的扫描频率,以提升效率。

cpython内存管理 python内存管理简称_Python_06