![582f42f99393f15023ce903ad20c01c0.png](https://i-blog.csdnimg.cn/blog_migrate/1edcee6a38320e60ce590f9b965c41e3.jpeg)
不是所有对象都有被 GC 照顾的殊荣,只有那些胸怀广大的 “容器” 才能被纳入 GC 的 “小圈圈”。我们已经直到所有在 GC 中的内存在创建之初,就被额外增加了一个包含 _gc_next 和 _gc_prev 的两个指针,这俩兄弟显而易见是在维护一个双向链表。
为了找到这个链表的位置,我们首先顺着 _PyObject_GC_TRACK 一层层抽丝剥茧,看看一个对象是如何被追加到某个链表中的,最终在这里找到了:
// include/internal/pycore_object.h:27
每当 Python 创建一个新的容器对象,其会立刻被插入 GC 的 generation0 链表尾部,从代码中的插入操作来看,这是一个当前运行时(PyRuntime)中的双向链表。generation0 的结构看起来这样:
![2afe290f768d1ef396d8dfc4c5276c6b.png](https://i-blog.csdnimg.cn/blog_migrate/c77dfcfe7760ed6e31b40128d84a6c48.png)
gc.generation0 的类型也是 GC_HEAD, 它充当了链表的头部和哨兵(sentinal)节点,这是一种典型的链表操作方法。Python 中利用双向链表将所有 GC 对象链接成一个环。
新创建的 GC 对象被链入了 gc.generation0,如果了解过常用 GC 算法就会想到,这里表示的是 “分代垃圾回收” 算法中的 0 代内存,那想必还有其他代内存了。这种直觉是正确的,可以在这里看到 Python 中内存分代的规划:
//Modules/gcmodule.c:128
可以清楚的看到, Python 采用了 3 代内存回收策略,其中 0 代的触发阈值是 700, 即 0 代对象超过 700 个就开始尝试回收内存,其他两代的阈值则是 10。使用分代垃圾回收策略的还有 C#、Java 等等,使用比较常见的垃圾回收策略,分代回收的主要目的是减少 GC 操作,提高虚拟机运行效率,其基本思想简而言之:
- 年轻代来去匆匆;
- 中老年安土重迁。
那些已经存在很久的对象更可能继续存在下去,刚创建的对象则很可能很快就被抛弃。
需要注意的是,Python 3.8 中 GC_HEAD 的两个指针在不同时刻可能被用来表示其他信息。
明天继续了。
关注我,了解程序员的烧脑日常,还有开源 Python 教程。
![bea6233048fabac8af5e8b238f019f9f.png](https://i-blog.csdnimg.cn/blog_migrate/feffd92008cec6bfb6ccfef04f4ce311.jpeg)