python gc内存_深入Python(28)-GC 中的内存

本文探讨了Python中GC(垃圾回收)如何处理容器对象的内存管理,特别是循环引用问题。通过分析List对象的创建过程,揭示了GC在内存分配中的角色。GC使用PyGC_Head结构,一个包含双向链表节点的附加信息,用于管理容器对象。Python 3.8版本对PyGC_Head进行了优化,以提高内存效率,但这也降低了代码的可读性。作者提醒读者,GC管理的对象结构包含PyGC_Head,并预告将进一步讲解相关细节。
摘要由CSDN通过智能技术生成

我们已经知道 Integer、String 等简单 Object 可以完全通过引用计数完成内存管理,List、Tuple、Dict 等对象被称为容器(container),其中又可能包含其他容器而形成 “循环引用”,使得引用计数方案失效。GC 是引用计数方案的补充。从之前研究过的 List 对象中,可以看到其与 GC 打交道的影子:

// Objects/listobject.c:179

// 先在 GC 中创建 ListObjectop = PyObject_GC_New(PyListObject, &PyList_Type);

// Objects/listobject.c:197// 让 GC 开始跟踪该对象_PyObject_GC_TRACK(op);

对比之前的 LongObject 的创建:

// Objects/longobject.c:275

// 直接在内存池中申请对象result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) +

size*sizeof(digit));

显然,在 GC 中创建的 ListObject 与直接在内存中创建的 LongObject 有一些区别,否则 ListObject 的创建就没必要多此一举使用 PyObject_GC_New 接口了。如果一直深入 PyObject_GC_New 函数,最后就可以看到这样的代码:

// Modules/gcmodule.c:1952_PyObject_GC_Alloc(int use_calloc, size_t basicsize)

{

// 忽略一些无关代码

// 实际申请的内存中额外申请了一块 PyGC_Head size = sizeof(PyGC_Head) + basicsize;

// 申请内存 if (use_calloc)

g = (PyGC_Head *)PyObject_Calloc(1, size);

else

g = (PyGC_Head *)PyObject_Malloc(size);

// 其他代码}

注意,Python 3.8 中此处代码与 2.5 版本有一些差异。

可以看到 PyObject_GC_New 最终调用的其实还是 PyObject_* 接口,区别在于,GC 管理下的内存增加了一些附加信息 PyGC_Head,从名称也可以清除的看出来这是一段仅与 GC 相关的信息,其结构:

// Include/cpython/objimpl.h:45

typedef struct {

uintptr_t _gc_next;

uintptr_t _gc_prev;

} PyGC_Head;

倒是非常简单,显然是一个双向链表节点的前、后指针,大致可以猜的到,GC 通过双向链表管理这些 containers。

同样的,Python 3.8 对 PyGC_Head 做了一些优化,一些 2.5 版本独立存在的结构体成员被复用到这两个指针中,后面我们会看到是如何利用这两个指针复用表示 ref_cnt、obj unreachable、collecting 等诸多信息。由于 Object 是 Python 中非常普遍的概念,这种内存优化实际上降低了代码的可读性,但是当存在大量对象时,这种方式对内存使用率的提升是可观的。

综合目前已知的信息,我们可以确定,这些在 GC 管理下的对象结构应该看起来这样,希望你还记得 PyObject 的结构,如果忘记了可以翻看前文:

明日继续。

关注我,了解程序员的烧脑日常,还有开源 Python 教程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值