码农高天揭Python内存陷阱 栈帧循环引用或锁定整条调用链

【python】引用计数机制中循环引用可能带来的问题 — 码农高天

码农高天

Python的栈帧对象因囊括全量局部变量与上级指针,一旦被持久化对象持有即会锁死整条调用链内存,必须通过上下文管理器主动解引用以防隐蔽泄漏。

AI-assisted summary of 【python】引用计数机制中循环引用可能带来的问题 — original source. Check the original for full context.

Source published:

Key ideas

  • Python 内存管理的核心基础是引用计数,绝大多数对象在引用计数归零时会立即触发内存释放,但其无法自行处理循环引用。 (0:00)
  • 虽然 Python 的垃圾回收(GC)机制能打破循环引用,但 GC 存在触发滞后性以及遍历复杂对象引用图带来的高昂算法计算成本。 (2:30)
  • 持有 Frame 等底层解释器对象会连锁保活整个调用链及局部变量,码农高天在 Python 3.13 的 BDB 调试器开发中曾因持久化 Frame 引发严重内存泄漏,最终通过上下文管理器重构解决。 (5:00)
  • 工程实践中无需因噎废食而试图完全手动管理 Python 内存,但在涉及 Frame、全局长生命周期对象或大型缓存时,应具备规避循环引用的意识。 (5:00)

Detailed notes

## Background 该视频由 YouTube 频道“码农高天”于 2025 年 2 月 4 日发布。视频围绕 Python 的垃圾回收机制展开,重点探讨了其核心基石——引用计数机制无法彻底解决的循环引用问题,以及循环引用的成因、潜在隐患与规避策略。

## TL;DR 视频深入剖析了 Python 依赖引用计数进行自动内存管理的基本原理,并用实例说明了对象引用归零即刻释放的机制。随后,作者揭示了当出现对象自引用或环状依赖时,常规计数失效、必须依赖后备垃圾回收器介入所带来的回收滞后与性能开销。结合自身在 Python 3.13 标准库 BDB 调试器开发中意外持有栈帧(Frame)引发级联泄漏的真实案例,作者给出了既不过度早熟优化、又在面对复杂长生命周期资源时保持警惕的工程实践建议。

## Atmosphere & style 视频风格清晰利落、深入浅出,带有鲜明的资深核心开发者技术分享质感。讲解过程摒弃了枯燥的说教,通过精简的控制台代码演示与亲历踩坑故事的无私复盘,将抽象晦涩的底层内存机制拆解得直观易懂。整体氛围务实而温和,既展现了技术底层严密推演的理性之美,又传递出不因噎废食、尊重语言设计哲学的从容工程态度。

## Bulletin - [00:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=0s) Python 内存管理的核心基础是引用计数,绝大多数对象在引用计数归零时会立即触发内存释放,但其无法自行处理循环引用。 - [02:30](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=150s) 虽然 Python 的垃圾回收(GC)机制能打破循环引用,但 GC 存在触发滞后性以及遍历复杂对象引用图带来的高昂算法计算成本。 - [05:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=300s) 持有 Frame 等底层解释器对象会连锁保活整个调用链及局部变量,码农高天在 Python 3.13 的 BDB 调试器开发中曾因持久化 Frame 引发严重内存泄漏,最终通过上下文管理器重构解决。 - [05:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=300s) 工程实践中无需因噎废食而试图完全手动管理 Python 内存,但在涉及 Frame、全局长生命周期对象或大型缓存时,应具备规避循环引用的意识。

## Key takeaways - [00:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=0s) Python 采用自动内存管理体系,其垃圾回收最核心的基础机制是引用计数。 - [00:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=0s) 当对象的局部变量离开作用域被销毁且无其他引用时,引用计数降为 0 并立即调用 __del__ 释放内存。 - [00:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=0s) 若对象被全局变量持续持有,其引用计数无法归零,只有在全局作用域销毁或显式调用 del 时才会被回收。 - [02:30](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=150s) 对象的属性指向自身(如 self.myself = self)会构成循环引用闭环,导致常规引用计数永远无法降为 0。 - [02:30](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=150s) Python 配备了专门的循环引用检测垃圾回收算法作为兜底,可通过 gc.collect() 强制清理不可达的循环引用对象。 - [02:30](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=150s) 循环引用的首要问题在于回收滞后性,GC 具有固定的触发周期,使无用内存无法被立即释放。 - [02:30](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=150s) 实际项目中的循环引用往往是由多层嵌套构成的长链条有向图,遍历检测此类结构会给 GC 带来显著的性能开销。 - [05:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=300s) 通过 sys._getframe() 获取的 Frame 对象包含当前栈帧的所有全局、局部变量及指向上一级栈帧的指针,极易造成大规模级联保活。 - [05:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=300s) 码农高天在 Python 3.13 调试器 BDB 中由于持久化 Frame 导致整个调用链内存泄漏,最终借助上下文管理器在退出时清空引用得以修复。 - [05:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=300s) 复杂业务逻辑中出现循环引用是不可避免的正常现象,开发者无需在日常开发中对其过度焦虑。 - [05:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=300s) 使用 Python 时无需违背语言设计去全面手动管理内存,但面对高危的大型系统资源时应顺手规避隐式循环引用。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=0s) Python 内存管理基石:引用计数与垃圾回收

Python 作为一门高级编程语言,允许开发者随时随地创建新对象,而这些对象显然都需要占用内存空间。与 C 或 C++ 这种需要显式管理内存的语言截然不同,Python 的内存分配与释放并非由用户直接手动控制,而是依托一套自动机制来回收不再使用的内存,这一机制统称为垃圾回收(Garbage Collection,GC)。在 Python 的垃圾回收体系中,最为核心的地基就是“引用计数”(Reference Counting)。

为了直观展示引用计数的工作方式,码农高天展示了一段基础代码:定义一个带有 `__del__` 方法的类 `A`,该方法会在对象即将被销毁、释放内存时自动触发。在函数 `f()` 内部,实例化了一个 `A` 的对象并赋值给局部变量 `o`;在主程序中调用 `f()` 并在其后打印 `"end"`。运行结果显示,控制台先输出了 `A` 的析构信息,随后才打印出 `"end"`。这是因为局部变量 `o` 仅存在于函数 `f()` 的作用域内,当函数返回时局部变量 `o` 被销毁,新对象的引用计数随即归零。Python 借此判定已经没有任何变量在引用该对象,于是立刻将其从内存中清除。

如果修改代码,把 `o` 作为 `f()` 的返回值并赋给全局变量 `r`,打印顺序便会彻底反转:先打印 `"end"`,最后才触发析构。其原因在于全局变量 `r` 一直持有该对象的引用,只有在整个模块执行完毕、全局作用域销毁时,引用计数才归零。若在打印 `"end"` 之前显式调用 `del r` 删去全局引用,对象则会再次提前释放。绝大多数情况下,Python 内部通过极为高效简单的加减法来统计引用数,一旦减到 0 就触发回收。码农高天评价道:“对于 Python 中绝大部分对象,Python 在运行这些代码的时候都会数着这个对象身上有多少个引用,在引用到 0 的时候来释放这个对象。”

```mermaid flowchart TD accTitle: Python 引用计数生命周期 accDescr: 展示对象创建、引用增加、作用域销毁导致计数归零并释放内存的流程

subgraph Scope["函数 f() 作用域"] Obj["对象 A 实例"] VarO["局部变量 o"] VarO -->|"引用计数 = 1"| Obj end

subgraph Exit["函数执行结束"] VarO -.->|"变量销毁"| Gone["o 离开作用域"] Obj -->|"引用归零 (count = 0)"| Free["触发 __del__ / 释放内存"] end ```

[02:30](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=150s) 循环引用的产生与垃圾回收算法的代价

引用计数机制虽然直观高效,但它存在一个致命弱点,那就是“循环引用”(Reference Cycle)。如果在类 `A` 的 `__init__` 初始化方法中加入一行代码 `self.myself = self`,就会使对象自身的属性指向自身,形成一个最简单的自引用闭环。在这种情况下,只要该对象存在,其内部属性就必然存在并持续引用该对象自身。即便外部通过 `del` 删除了所有指向该对象的外部变量,其内部引用计数永远无法降为 0,导致该对象在常规引用计数机制下无法被及时释放,一直滞留在内存中。

不过,循环引用在实际开发中非常普遍,Python 并不会因此直接全面发生内存泄漏。Python 引入了专门的垃圾回收算法来检测循环引用,分析一组对象是否已经脱离了外部可达性、只剩下彼此之间的内部引用;若是,垃圾回收器便会介入将其强行释放。在代码中显式调用 `gc.collect()` 即可立即观察到这些处于循环引用中的垃圾对象被成功清理。

既然有这套后备机制兜底,开发者为何还需要关注循环引用?码农高天指出了两个核心问题: 1. **滞后性**:垃圾回收并非时刻都在运行,而是存在固定的触发间隔。这就意味着已经无用的内存会被滞后霸占一段时间,无法即刻交还给系统。 2. **性能开销**:现实项目中的循环引用往往非常隐蔽复杂,例如 A 引用 B、B 引用 C、C 引用 D、D 引用 E,最终形成一个长链条循环。遍历并检测这种复杂的有向引用图所消耗的算法计算成本高昂,不必要的循环引用会显著增加垃圾回收器的运行负担。

码农高天强调:“检查循环引用的算法不是一个非常简单的算法,它是一个比较耗时比较复杂的算法,因为大多数时候我们真正的程序里面的循环引用并没有我们写的这么明显。”

[05:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=300s) 隐藏的 Frame 泄漏陷阱与工程实践总结

除了简单的对象自引用,更危险的场景隐藏在解释器底层对象的交互中。例如,类 `A` 在初始化时获取并持有了当前栈帧(`sys._getframe()`)的引用,而看似毫不相干的临时局部变量 `B` 也在同一函数中被创建。当函数返回并保留 `A` 时,由于 Python 的 Frame 对象结构非常庞大——它不仅囊括了当前栈帧下的所有全局变量和局部变量,还包含了指向上一级调用栈帧的指针——这就导致当前调用链上所有涉及的对象全部被强行“保活”,根本无法通过引用计数正常释放。

码农高天分享了自己在 Python 3.13 核心开发中踩过的真实案例:在给 Python 标准库调试器 BDB(Debugger Base)增加功能时,他记录了触发 Debugger 的当前 Frame,用以区分哪些是调试器内部代码、哪些是用户业务代码。然而,由于另一处改动将上一次运行使用的 PDB 实例持久保存在了程序中,这一行 Frame 记录导致该栈帧及该栈帧往上的整条调用链、所有局部变量被永久锁定在内存里,酿成了严重的内存泄漏问题。最终,他将其重构为一个上下文管理器(Context Manager),在进入时记录 Frame,退出时立刻清空解引用,从而根治了该泄漏。

```mermaid flowchart LR accTitle: Frame 级联引用导致无法释放的结构图 accDescr: 展示持久对象持有 Frame 引用进而锁定所有局部变量的传播链条

subgraph LongLived["长生命周期对象 (如 BDB / PDB)"] Holder["Debugger / 持久对象"] end

subgraph CallStack["调用栈帧信息"] F1["当前 Frame"] F0["上一层 Frame"] end

subgraph Variables["局部变量堆"] V1["局部变量 B"] V2["其他业务数据"] end

Holder -->|"持有引用"| F1 F1 -->|"f_back 指针"| F0 F1 -->|"包含 locals"| V1 F1 -->|"包含 locals"| V2 ```

最后,码农高天对内存管理与循环引用给出了务实的总结与建议: - **循环引用并非洪水猛兽**:只要编写稍具规模的复杂程序,循环引用必然会出现,完全不需要因噎废食,更无需在写每一行代码时都提心吊胆。 - **无需强行手动管理**:“我们既然语言已经选择了 Python,就没有必要试图去完全手动管理内存,在大部分情况下它其实不会影响你程序的运行。” - **建立防范意识**:开发者脑中应当保留这根弦,清晰理解引用计数和垃圾回收背后的机制。当遇到持有大型系统资源(如 Frame 对象、大型缓存、长生命周期单例)时,若能通过手动清理引用或使用上下文管理器等简单手段化解循环引用,就应顺手规避,避免造成深层的性能惩罚或隐蔽的内存泄漏。

## Notable quotes > [05:00](https://www.youtube.com/watch?v=sy0FXlhnX0c&t=300s) 我们既然语言已经选择了 Python,就没有必要试图去完全手动管理内存,在大部分情况下它其实不会影响你程序的运行。 — 码农高天

## Mentioned Python, 码农高天, C, C++, BDB, PDB, Python 3.13

## Video description Python是一门有垃圾回收机制的语言,而这个机制的核心是引用计数。然而引用计数并不能解决一切,循环引用就是最常见的一个问题。我们这期视频简单聊一下循环引用是什么,它会造成什么问题,以及什么情况下应该尝试规避它。

[Source](https://www.youtube.com/watch?v=sy0FXlhnX0c)

中文简报

码农高天揭Python内存陷阱 栈帧循环引用或锁定整条调用链 Python的栈帧对象因囊括全量局部变量与上级指针,一旦被持久化对象持有即会锁死整条调用链内存,必须通过上下文管理器主动解引用以防隐蔽泄漏。 视频深入剖析了 Python 依赖引用计数进行自动内存管理的基本原理,并用实例说明了对象引用归零即刻释放的机制。随后,作者揭示了当出现对象自引用或环状依赖时,常规计数失效、必须依赖后备垃圾回收器介入所带来的回收滞后与性能开销。结合自身在 Python 3.13 标准库 BDB 调试器开发中意外持有栈帧(Frame)引发级联泄漏的真实案例,作者给出了既不过度早熟优化、又在面对复杂长生命周期资源时保持警惕的工程实践建议。 Python 内存管理的核心基础是引用计数,绝大多数对象在引用计数归零时会立即触发内存释放,但其无法自行处理循环引用。 (0:00) 虽然 Python 的垃圾回收(GC)机制能打破循环引用,但 GC 存在触发滞后性以及遍历复杂对象引用图带来的高昂算法计算成本。 (2:30) 持有 Frame 等底层解释器对象会连锁保活整个调用链及局部变量,码农高天在 Python 3.13 的 BDB 调试器开发中曾因持久化 Frame 引发严重内存泄漏,最终通过上下文管理器重构解决。 (5:00) 工程实践中无需因噎废食而试图完全手动管理 Python 内存,但在涉及 Frame、全局长生命周期对象或大型缓存时,应具备规避循环引用的意识。 (5:00)

Reading summary

Python 以引用计数作为内存管理的核心基石,虽然内置的垃圾回收算法能兜底清理循环引用,但循环引用仍会带来释放滞后和算法开销;在涉及栈帧(Frame)等底层大对象时,不当引用更易引发隐蔽的级联内存泄漏。 引用计数机制与即时内存释放 (0:00) Python 的自动内存管理依托于引用计数(Reference Counting)。当在函数作用域内创建对象并赋给局部变量时,随着函数执行完毕、局部变量销毁,对象的引用计数归零,解释器会立即调用其析构方法(`__del__`)并释放内存。相反,若将对象传递给全局变量等长生命周期载体,对象就会持续存活,直到全局作用域销毁或被显式执行 `del` 操作。在绝大多数日常场景中,Python 依靠对引用数的简单加减法,即可高效、即时地完成对象清理。 循环引用的代价与垃圾回收兜底 (2:30) 当对象形成内部自引用(例如 `self.myself = self`)或多对象间环状依赖时,即使外部指向该对象的变量全部被移除,其引用计数也无法减为 0,常规机制随即失效。尽管 Python 配备了专门的垃圾回收算法,能够分析出脱离外部可达性的孤岛对象并强行清理(例如显式调用 `gc.collect()`),但该机制并非无代价的免费午餐。码农高天指出,垃圾回收存在触发间隔,导致无用内存在一段时间内被滞后霸占;同时,遍历检测由长链条对象构成的复杂有向引用图需要消耗高昂的算法计算成本。 栈帧引用导致的隐蔽级联泄漏 (5:00) 除了显而易见的对象自引用,持有解释器底层对象(如 `sys._getframe()` 获取的 Frame 栈帧)往往潜藏更大的内存风险。Frame 对象结构庞大,不仅保存了当前环境的局部变量与全局变量,还包含指向上一级调用栈的指针,使得整条调用链上的所有业务数据被强行保活。码农高天以开发 Python 3.13 调试器 BDB 的经历说明,当长生命周期的 PDB 实例持久保存了 Frame 记录后,便导致了整条调用栈及变量无法释放的严重内存泄漏;该问题最终通过将其改造为上下文管理器、在退出时即刻清空解引用才得以解决。 循环引用在复杂程序中不可避免,开发者无需因噎废食而试图完全手动管理 Python 内存。但在面对 Frame 栈帧、大型缓存或长生命周期单例等大对象时,应建立防范意识,借助上下文管理器或手动置空等手段及时化解引用环,避免性能惩罚与深层泄漏。

Source quotes

[5:00] 码农高天: 我们既然语言已经选择了 Python,就没有必要试图去完全手动管理内存,在大部分情况下它其实不会影响你程序的运行。

Original transcript

[0:00] 今天我们来聊一下循环引用啊reference cycle在聊循环引用之前呢我们首先聊一下循环引用里面这个引用是什么意思我们知道在python中你可以随时随地的这样建立一个新的object的那这个新的object显然是需要内存的可是和c,c++这样的语言不一样python是没有显示的内存管理的或者说Python的内存管理并不是由用户直接控制的那我们就需要一个机制自动把这些已经分配的内存给释放掉这个机制我们一般叫垃圾回收Gurbage Collection而Python的垃圾回收是以引用技术为核心的我们先来看左边这个例子我定义了一个class A,在class A 里面有del这个method这个method是在这个对象即将被释放的时候调用的在函数F里我们建立了一个A的object把它复制给了O然后我们调用F函数打印一个end我们看运行的结果这个objectO先被释放了然后才打印的end那python怎么知道要释放这个object呢?这就是引用技术的功劳在python建立完object之后它就会数着有多少其他的变量或者对象正在使用这个形件的对象比如这里我们把它复制给O了这个对象的引用技术就是E有一个变量O正在用着它那这个O是一个F函数的局部变量所以当F函数返回的时候这个O就没有了这个时候我们新建的对象上面的引用就到了0Python就知道没有任何东西需要这个对象了于是就把它给释放了我们看假如把这个代码改一下我们把这个O作为函数的结果返回然后复值给R这个时候打印的两行就反过来了是先打印的and然后打印的这个object deleted这是因为全局变量R也引用了这个对象所以这个对象还不能被释放只有到整个module运行结束的时候这个全局变量R也用不着了这个对象才会被释放我们可以提前声明这个R变量已经用不着了也就是写一个delr 这样在第10行的时候这个对象上就没有引用了它就会被释放我们可以看到结果这个对象先被释放的然后再打印end对于拍葬中绝大部分对象Python在运行这些代码的时候都会数着这个对象身上有多少个引用在引用到0的时候来释放这个对象那这个引用技术其实是一个很容易理解的规则同时它也非常高效因为你多一个引用只需要做一个加法嘛然后减法减到零的时候释放嘛它是一个非常优质的机制然而这个机制有一个问题啊就是所谓的循环引诱我们看我们加了一个init函数这个init函数里面有一句话叫self.myself等于self那这句话一写是不是就是我这个对象本身被我这个对象的属性所引用了这就形成了一个循环啊我自己引用自己那可以想见这个对象的引用数就永远不可能到0了因为只要这个对象存在这个对象的属性就存在这个对象的属性就在引用这个对象无论如何他也到不了零所以我们看哪怕还是刚才的那段代码我们运行一下ObjectDeleted也是最后才完成的也就是尽管我们已经删掉了所有的外部引用但是由于内部引用的存在这个对象还一直保持在内存里当然你说那这个世界就这么完蛋了吗也不是啊这个循环引用在我们实际的编程中的其实并不罕见如果有一个循环引用就泄露内存的话那成全篇写了在垃圾回收这个机制里面有一些特定的算法来检查这些循环引用从而判断一个对象是不是只剩下这些内部引用了如果是的话 它也可以正常释放那我们平时所说的垃圾回收或者说是那些真正占用时间的垃圾回收其实就是在检查这些循环应用我们看这里我们可以手动触发垃圾回收GC.collect这时候我们就会发现在垃圾回收的时候这个垃圾就被回收了这个对象所占用的内存就被释放了那既然有这种机制我们为什么还要在意这件事呢你就等著垃圾回收给他回收了不就完了吗这里面有两层原因第一垃圾回收你不可能一直做对吧它总是有一个间隔的啊你隔一这儿做一次隔一这儿做一次也就是说你的内存的释放会有一个滞后性这些内存你明明现在用不着了但是你还霸占了他们一段时间第啊我们不难想象检查循环引用的算法不是一个非常简单的算法它是一个比较耗时比较复杂的算法因为大多数时候我们真正的程序里面的循环引用并没有我们写的这么明显他可能是A用了B,B用了C,C用了D,D用了E,然后一串下去回头 [5:00] 已经用了 A 对吧那无畏的循环引用会增加我们垃圾回收的负担所以在有些时候我们可以手动的避免这样的循环引用比如说我举个非常简单的例子我们这个对象啊什么时候用完了我可以调这个finish 几书这个finish 几乎里面把这个 myself 清空这样我们就可以看到这个对象就被正常的通过引用技术回收了这个例子很直白啊并不是说大家写的每一行代码都要去想哎呀我这个东西是不是循环引用了怎么怎么样但是脑子里面有这根弦是有帮助的我们举个更有趣的例子我们看这个A在init里面他拿到了一个这个frame的引用而B就是刚才的A在F函数里面呢我们建立了一个A的object返回了我们也建立了一个B的Object但是这个B的ObjectOR非常无辜我们只是建立了它复制给OR了而已它应该是一个很局部的东西然而我们看这段代码的运行结果A跟B都是在所有程序运行完之后才被释放的这是因为在python里面frame这个东西包含了太多的信息它包含了这个frame下的所有局部变量全局变量它还包含了指向上一个frame的指针也就是说只要你残留一个frame的引用你基本上就保留了从这个frame开始往上的每一个frame的所有变量的引用所有的局部变量所有这些局部变量引用的其他变量都暂时无法释放那像这样的循环引用可能就是我们需要避免的那我们虽然这期视频聊的是循环引用但是我们大家想一下其实循环引用和内存泄漏是非常接近的循环引用我们还可以通过垃圾回收的机制把这些东西收回如果这个frame object被我遗忘在了某一个对象的 attribute 里而那个对象又活了很久他就意味着所有这个引用链上的东西都不能被释放可实际上有可能那个对象已经不需要这个frame object所以虽然我们聊的是循环引诱但是同样的思想我们也可以把它放到一个对象里面要不要去手动解引用这件事也就是这个数据我需不需要一直记着这里给大家一个真实的例子在Python3.13里面我给BDB加了这么一个东西我在BDB里面记录了一下触发Debugger的这个frame这样我在进行一些操作的时候就知道哪一些是debugger的frame哪一些是我运行的用户代码的frame然而这就导致这个frame被永久的记录到了BDB里而由于我的另外一个修改上一次使用的PDB对象是会被一直保存在程序里的也就是说这两件事加起来实际上是有一个比较严重的内存泄漏的很多的frame object和这些frame里面的局部变量都没有办法被释放因为有一个东西一直在无谓的引用着他们那实际上这个enterframe我只有这一次运行的时候BDB内部会需要这个信息所以我们就把它改成了一个context manager每次进来的时候复制结束的时候清空这样就防止了这种引用泄漏好那回到我们今天内容总结一下循环引用这个东西不是洪水猛兽你只要正常写程序写的稍微复杂一点它一定会出现我们既然语言已经选择了Python就没有必要试图去完全手动管理内存在大部分情况下它其实不会影响你程序的运行但是我们最好了解一下它背后的机制以及在什么情况下它可能会产生比较大的问题然后在这些情况下脑子里想着这么个事儿如果能很简单的避免这个问题就尝试避免一下好那这一期视频这里喜欢的话点点关注 并且观众 三连哦优优独播剧场——YoYo Television Series Exclusive