CPython 垃圾收集器检测循环引用详解

目录
  • CPython 中的垃圾收集器
    • 检测循环引用

CPython 中的垃圾收集器

CPython 的垃圾收集器(简称GC)是 Python 内置的为了解决循环引用问题的方法。默认情况下,它总是在后台运行,并且每隔一段时间就会发挥它的魔力,所以你不必担心循环引用物会堵塞你的内存。

垃圾收集器被设计为从 CPython 的工作内存中找到并删除循环引用对象。它通过以下方式完成这一工作。

  • 检测循环引用的对象
  • 调用最终的 __del__ 方法
  • 它从每个对象中删除指针(以此来解决循环问题),只有当循环在步骤 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。 这个数字是由垃圾收集器本身输出的,它告诉我们有多少对象被移除。

参考链接:

https://anvil.works/articles/pointers-in-my-python-3

以上就是CPython 垃圾收集器检测循环引用详解的详细内容,更多关于CPython 垃圾收集器的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python解析器Cpython的GIL解释器锁工作机制

    目录 本节重点 一 引子 二 GIL介绍 三 GIL与Lock 四 GIL与多线程 五 多线程性能测试 本节重点 掌握Cpython的GIL解释器锁的工作机制 掌握GIL与互斥锁 掌握Cpython下多线程与多进程各自的应用场景 本节时长需控制在45分钟内 一 引子 定义: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing

  • 利用一个简单的例子窥探CPython内核的运行机制

    我最近花了一些时间在探索CPython,并且我想要在这里分享我的一些冒险经历.Allison Kaptur的excellent guide to getting started with Python internals有一点啰嗦,我想逐步介绍我自己的探索过程会更加有条理性,这样也许其他好奇的Python使用者可以跟着一起做. 1.注意到了一些奇怪的事情 一开始,我只是设置好Nose对一些我写的Python 3代码进行测试.当我运行这些测试的时候,我得到了一个不可思议的错误信息:"TypeErr

  • Cpython解释器中的GIL全局解释器锁

    1.什么是GIL全局解释器锁 GIL:Global Interpreter Lock,意思就是全局解释器锁,这个GIL并不是Python的特性,他是只在Cpython解释器里引入的一个概念,而在其他的语言编写的解释器里就没有GIL,例如:Jython,Pypy等 下面是官方给出的解释: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from exe

  • CPython 垃圾收集器检测循环引用详解

    目录 CPython 中的垃圾收集器 检测循环引用 CPython 中的垃圾收集器 CPython 的垃圾收集器(简称GC)是 Python 内置的为了解决循环引用问题的方法.默认情况下,它总是在后台运行,并且每隔一段时间就会发挥它的魔力,所以你不必担心循环引用物会堵塞你的内存. 垃圾收集器被设计为从 CPython 的工作内存中找到并删除循环引用对象.它通过以下方式完成这一工作. 检测循环引用的对象 调用最终的 __del__ 方法 它从每个对象中删除指针(以此来解决循环问题),只有当循环在步

  • iOS block循环引用详解及常见误区

    Block循环引用 什么情况下block会造成循环引用 ARC 情况下 block为了保证代码块内部对象不被提前释放,会对block中的对象进行强引用,就相当于持有了其中的对象,而如果此时block中的对象又持有了该block,就会造成循环引用. 常见误区 误区一.所有block都会造成循环引用 在block中,并不是所有的block都会循造成环引用,比如UIView动画block.Masonry添加约束block.AFN网络请求回调block等.     1. UIView动画block不会造

  • iOS如何巧妙解决NSTimer的循环引用详解

    一 发现问题 我们都知道NSTimer采用target-action的方式,通常target又是类本身,我们为了方便又把NSTimer声明为属性变量,这样就难免会造成循环引用(需要反复执行计时任务时,如果是单次的任务就不会造成循环引用). 例如: _timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(startTimer) userInfo:nil repeats:YES]; 深入理

  • 关于NodeJS中的循环引用详解

    最近在用node的时候排查一个问题排查了半天,最终发现是循环引用导致的问题,故在此记录一下. 场景复现 出现问题场景比较简单,一共四个类: parent.ts child.ts child_2.ts util.ts export abstract class Parent { abstract hello(): string; } import {Parent} from "./parent"; export class Child extends Parent { hello():

  • Swift中优雅处理闭包导致的循环引用详解

    前言 Objective-C 作为一门资历很老的语言,添加了 Block 这个特性后深受广大 iOS 开发者的喜爱.在 Swift 中,对应的概念叫做 Closure,即闭包.虽然更换了名字,但是概念和用法还是相似的,就算是副作用也一样,有可能导致循环引用. 下面我们用一个例子看一下,首先我们需要第一个控制器(FirstViewController),它所做的就是简单的推出第二个控制器(SecondViewController). class FirstViewController: UIVie

  • Windows bat脚本之for循环用法详解

    Windows bat脚本的for语句基本形态如下: 在cmd窗口中:for %I in (command1) do command2 在批处理文件中:for %%I in (command1) do command2 之所以要区分cmd窗口和批处理文件两种环境,是因为在这两种环境下,命令语句表现出来的行为虽然基本一样,但是在细节上还是稍有不同,最明显的一个差异就是:在cmd窗口中,for之后的形式变量I必须使用单百分号引用,即%I:而在批处理文件中,引用形式变量I必须使用双百分号,即%%I.为

  • C语言结构体指针引用详解

    目录 指向结构体变量的指针 指向结构体数组的指针 结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针. 指向结构体变量的指针 前面我们通过"结构体变量名.成员名"的方式引用结构体变量中的成员,除了这种方法之外还可以使用指针. 前面讲过,&student1 表示结构体变量 student1 的首地址,即 student1 第一个项的地址.如果定义一个指针变量 p 指向这个地址的话,p 就可以指向结构体变量 student1 中的任意一个成员. 那么,这个指针变量定义成

  • Python 循环结构详解

    目录 一.While循环 二.While…else…循环 三.for循环 四.for…else…循环 五.循环体结束语句 六.嵌套循环 前言: 在编写代码时如果能够简化一个程序,把重复的代码形成公共部分,利用语法来控制,对代码进行循环往复的执行,这就形成了循环结构逻辑. 一.While循环 While循环的语法格式如下: while 条件表达式 :    代码段(包含改变计数器值的语句) while 关键词后面紧跟的条件表达式决定了循环是够进行下去. 下面用while语句实现求1-100的累加和

  • Qt+FFMPEG实现循环解码详解

    目录 一.结果 二.解码准备工作+循环解码相关操作 videodecode.h .cpp main.cpp 一.结果 可以设置延时函数-----遍历每一帧的信息进行打印 25(fps)*30(秒)=750帧 二.解码准备工作+循环解码相关操作 videodecode.h .cpp #ifndef VIDEODECODE_H #define VIDEODECODE_H #include <QObject> //当前C++兼容C语言 extern "C" { //avcodec

  • shell 循环命令详解

    目录 1. for 命令 1.1 for 命令的使用 1.2 for 命令面临的问题 1.3 更改字段分隔符 1.4 用通配符读取目录 2. c 风格 for 命令 3. while 命令 4. until 命令 5. 控制循环 5.1. break 命令 5.2. continue命令 1. for 命令 1.1 for 命令的使用 bash shell 提供了 for 命令,可以创建一个遍历一系列值的循环.每次一轮循环都使用其中一个值来执行已定义好的一组命令.下面是 bash shell 中

随机推荐