持续创作,加快生长!这是我参加「日新方案 10 月更文应战」的第 5 天,点击检查活动概况

CPython 中的废物收集器

CPython 的废物收集器(简称GC)是 Python 内置的为了处理循环引证问题的办法。默许情况下,它总是在后台运转,而且每隔一段时间就会发挥它的魔力,所以你不用忧虑循环引证物会堵塞你的内存。

废物收集器被规划为从 CPython 的作业内存中找到并删去循环引证目标。它经过以下方式完结这一作业。

  1. 检测循环引证的目标
  2. 调用最终的 __del__ 办法
  3. 它从每个目标中删去指针(以此来处理循环问题),只有当循环在步骤 2 之后仍然是孤立的

在这个过程完结后,曾经在循环中的每个目标现在的引证计数都是 0 ,因而此目标将从内存中删去。

尽管它是主动作业的,但实际上咱们能够把它作为一个模块从规范库中导入。举例如下:

import gc

检测循环引证

CPython 的废物收集器会盯梢内存中存在的各种目标–但不是一切的目标。咱们能够实例化一些目标,看看废物收集器是否会收集它们。

>>> gc.is_tracked("a string")
False
>>> gc.is_tracked(["a", "list"])
True

如果一个目标能够包括指针,这就使它有才能构成循环引证结构的一部分–而这正是废物检测器存在的目的,即检测和拆除。在 Python 中这样的目标通常被称为 “容器目标”。

所以,废物收集器需要知道任何有或许作为循环引证的一部分而存在的目标。字符串不能,所以 “一个字符串 “不会被废物收集器追寻。列表(正如咱们已经看到的)能够包括指针,因而 ['a', 'list'] 被盯梢。

用户界说的类的任何实例也将被废物收集器盯梢,由于咱们总是能够在它们身上设置任意的特点(指针)。

>>> Wade = MyNameClass("Wade")
>>> gc.is_tracked(Wade)
True

所以,废物收集器知道一切有或许构成循环引证的目标。它怎样知道是否已经构成循环引证呢?

它也知道每个目标中的一切指针,以及它们所指向的位置。咱们能够看到这个动作。

>>> my_list = ["a", "list"]
>>> gc.get_referents(my_list)
['list', 'a']

get_referents 办法(也称为遍历办法)接纳一个目标,并回来它所包括的目标指针的列表(它的引证)。因而,上面的列表包括指向其每个元素的指针,这些元素都是字符串。

让咱们在一个目标的循环中看看 get_referents 办法(尽管还不是一个循环引证,由于这些目标仍然能够从命名空间中被拜访)。

>>> jane = MyNamedClass("Jane")
>>> bob = MyNamedClass("Bob")
>>> jane.friend = bob
>>> bob.friend = jane
>>> gc.get_referents(bob)
[{'name': 'bob', 'friend': <__main__.MyNamedClass object at 0x7ff29a095d60>}, <class '__main__

在这个循环中,咱们能够看到由 bob 指向的目标包括指向以下内容的指针:它的特点字典,包括 bob 的名字 (bob) 和它的朋友 (同样由 jane 指向的 MyNamedClass 实例) 。bob 目标也有一个指向类目标本身的指针,由于 bob.class 将回来那个类目标。

当废物收集器运转时,它检查它所知道的每个目标(也便是当你调用 gc.is_tracked 时回来True的任何目标)是否能够从命名空间抵达。它经过盯梢来自命名空间的一切指针,以及这些指针所指向的目标中的指针,以此类推,直到它建立起一切可从代码中拜访的东西的整个视图。

如果在做完这些之后,GC 发现存在一些不能从命名空间抵达的目标,那么它能够把这些目标清除去。

记住,任何仍在内存中的目标必须有一个非零的引证计数,否则它们会由于引证计数而被删去。对于那些无法抵达但仍有非零引证计数的目标,它们必须是循环引证的一部分,这便是为什么咱们如此关怀这些发生的或许性。

让咱们回到引证循环,jane 和 bob,经过从命名空间中移除指针,把这个循环变成一个循环的隔离。

>>> del jane
>>> del bob

现在,咱们已经了解了废物收集器所要处理的确切情况。咱们能够经过调用 gc.collect() 来触发手动废物收集。

>>> gc.collect()
Deleting Bob!
Deleting Jane!
4

默许情况下,废物收集器会每隔一段时间主动执行这个动作(由于越来越多的目标在CPython运转时被创建和毁掉)。

在上面的代码片段中,咱们看到的输出包括了来自 MyNamClass 的 __del__ 办法的打印语句,在最终有一个数字–在这个例子中,是 4。 这个数字是由废物收集器本身输出的,它告知咱们有多少目标被移除。

参考链接:

  • anvil.works/articles/po…