目前已经介绍了两种不可变类型 int 和 string,另外大致了解了一种容器 List,基本类型还差 Dict 没有涉及到,Dict 广泛应用于 Python 的方方面面,我打算稍晚再处理。通过阅读前面三种基本数据类型源码,大致可以看到 Python 的所有对象都生存在其内存管理系统之上。
关于 Python 的内存管理可以参考陈儒先生的 《Python 源码剖析》第 16 章。从目前接触的源码来看,最容易察觉的是 PyObject_Header 中的 ob_refcnt 在整个对象生命周期中的意义,例如 int、string 这样的简单对象可以在不借助 GC 的情况下完成内存回收,因为在这类对象的 “析构” 函数中会先减少引用计数 ob_refcnt,如果引用计数降到 0 才真正进行内存释放操作。这一类简单对象只需要管理好对象自身的引用即可,因此均使用 Python 封装的低级内存分配接口,比如 PyObject_MALLOC。
前面刚涉及的 List 对象,它作为一种对象容器,自身同时也对其他对象也存在引用,单纯的引用计数就已经不足以应对这种情况,比如循环引用的问题,这一类对象就需要借助 GC 实现内存管理。Python 的低级内存分配接口,实际上依然是系统级 malloc 函数族的简单代理,这一类高级数据类型的内存分配需要在 GC 中分配,可以看到 List 对象的内存分配通过PyObject_GC_New 接口分配,然后通过 _PyObject_GC_TRACK 将这一段内存添加到 GC 监视的链表中。
那么 PyObject_GC_New 与 GC 的关联到底在哪里呢?如果不断追踪这个函数,经过:
PyObject_GC_New -> _PyObject_GC_New -> _PyObject_GC_Malloc -> _PyObject_GC_Alloc
在 _PyObject_GC_Alloc 中有这样一段代码:
if (gcstate->generations[0].count > gcstate->generations[0].threshold &&
gcstate->enabled &&
gcstate->generations[0].threshold &&
!gcstate->collecting &&
!_PyErr_Occurred(tstate))
{
gcstate->collecting = 1;
collect_generations(tstate);
gcstate->collecting = 0;
}
gcstate 是当前 Python 解释器中的 gc 状态机,显然,每次尝试从 GC 中分配内存的时候,会检查当前的内存使用情况,一旦超过阈值就开始进行 “垃圾回收”,循环引用的问题也会在这里处理。
垃圾回收只是 Python 内存管理表面的一层,在底层内存层面采用了内存池技术,这是保证 Python 性能的关键所在。
明天继续。