python gc_python源码阅读笔记之GC

原标题:python源码阅读笔记之GC

引用计数法

增量:

各个对象的内部都有计数器。如果对象的引用数量增加,就在计数器上加1,否则减1

#define Py_INCREF(op) (

_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA

((PyObject*)(op))->ob_refcnt++)

下面的是含有NULL检查的宏

#define Py_XINCREF(op) do { if ((op) == NULL) ; else Py_INCREF(op); } while (0)

计数器如何避免溢出?

#define PyObject_HEAD

_PyObject_HEAD_EXTRA

Py_ssize_t ob_refcnt;

struct _typeobject *ob_type;

从这里可以看出ob_refcnt的数据类型

在pyport.h

#ifdef HAVE_SSIZE_T

typedef ssize_t Py_ssize_t;

#elif SIZEOF_VOID_P == SIZEOF_SIZE_T

typedef Py_intptr_t Py_ssize_t;

#else

# error "Python needs a typedef for Py_ssize_t in pyport.h."

#endif

是C里的ssize_t类型,这样就可以和各自CPU位数的指针的大小一样。因为有符号位,所以只有一半的数值能用非负整数表示,为啥计数器要使用负数呢?这是为了debug

#define _Py_CHECK_REFCNT(OP)

{ if (((PyObject*)OP)->ob_refcnt < 0)

_Py_NegativeRefcount(__FILE__, __LINE__,

(PyObject *)(OP));

}

减量操作

#define Py_DECREF(op)

do {

if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA

--((PyObject*)(op))->ob_refcnt != 0)

_Py_CHECK_REFCNT(op)

else

_Py_Dealloc((PyObject *)(op));

} while (0)

先将计数器减量,如果得出0以外的数值就调用_Py_CHECK_REFCNT(),为了防止意外

否则调用_Py_Dealloc()

#define _Py_Dealloc(op) (

_Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA

(*Py_TYPE(op)->tp_dealloc)((PyObject *)(op)))

tp_dealloc存储着释放各个对象的函数指针

大概的释放的调用如此

Py_DECREF

_Py_Dealloc

tupledealloc

PyObject_GC_Del

PyObject_FREE

PyObject_Free

插入计数处理

这些技术都是基于生成指向对象的引用时进行的,而这不一定是Python的对象

局部变量引用时,绝大多数情况都不用引用计数

python终结器的概念:

内置数据类型的对象是不能设置终结器的,能定义终结器的只有用户创建的类

循环引用垃圾回收:

容器对象:可能保留了指向其他对象的引用的对象

这些对象都分配的用于循环引用垃圾回收的头结构体

/* GC information is stored BEFORE the object structure. */

typedef union _gc_head {

struct {

union _gc_head *gc_next;

union _gc_head *gc_prev;

Py_ssize_t gc_refs;

} gc;

long double dummy; /* force worst-case alignment */

} PyGC_Head;

前两个用于双向链表,最后一个是用于复制

dummy的作用如源码注释:即使结构体gc的大小不合理。它也会将整个结构体PyGC_Head的大小对齐为long double型

由下述代码负责分配所有容器对象的函数

PyObject *

_PyObject_GC_Malloc(size_t basicsize)

{

PyObject *op;

PyGC_Head *g;

if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head))

return PyErr_NoMemory();

g = (PyGC_Head *)PyObject_MALLOC(

sizeof(PyGC_Head) + basicsize);/*此处可见,在分配对象时也额外分配了用于循环引用垃圾回收的头大小*/

if (g == NULL)

return PyErr_NoMemory();

g->gc.gc_refs = GC_UNTRACKED;/*GC_UNTRACKED存入用于循环引用垃圾回收的头内成员gc_refs,当出现这个标志时,GC会认为这个容器对象没有连接到对象链*/

generations[0].count++; /* number of allocated GC objects */

if (generations[0].count > generations[0].threshold &&

enabled &&

generations[0].threshold &&

!collecting &&

!PyErr_Occurred()) {

collecting = 1;

collect_generations();

collecting = 0;

}

op = FROM_GC(g);/*这个会偏移用于循环垃圾引用的头的长度,返回正确的对象地址,为了不区分容器对象和其他对象*/

return op;

}

#define GC_UNTRACKED _PyGC_REFS_UNTRACKED

gc_refs用负值做标志,这是为了节省空间

/* Get the object given the GC head */

#define FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))

追踪容器对象

这个宏放在各个容器对象新建立的时候,负责连接链表的操作

/* Tell the GC to track this object. NB: While the object is tracked the

* collector it must be safe to call the ob_traverse method. */

#define _PyObject_GC_TRACK(o) do {

PyGC_Head *g = _Py_AS_GC(o); *先从对象的开头地址开始,将头地址偏移相应的大小,取出用于循环引用垃圾回收的头*

if (g->gc.gc_refs != _PyGC_REFS_UNTRACKED)

Py_FatalError("GC object already tracked");

g->gc.gc_refs = _PyGC_REFS_REACHABLE; *修改gc_refs,表示可到达*

g->gc.gc_next = _PyGC_generation0; *拿出了连接所有容器对象的全局性容器对象链表,把对象链接到这个链表*

g->gc.gc_prev = _PyGC_generation0->gc.gc_prev;

g->gc.gc_prev->gc.gc_next = g;

_PyGC_generation0->gc.gc_prev = g;

} while (0);

#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)

同样的方式可以结束追踪对象;

/*** Global GC state ***/

struct gc_generation {

PyGC_Head head;

int threshold; /* collection threshold */

int count; /* count of allocations or collections of younger

generations */

};

这个结构体用于管理各代的容器对象,一旦count超过了threshold程序就会对这一代执行GC

0代 生成的容器对象的数量 - 删除的容器对象的数量

1代 0代经过GC的次数

2代 1代经过GC的次数

/* linked lists of container objects */

static struct gc_generation generations[NUM_GENERATIONS] = {

/* PyGC_Head, threshold, count */

{{{GEN_HEAD(0), GEN_HEAD(0), 0}}, 700, 0},

{{{GEN_HEAD(1), GEN_HEAD(1), 0}}, 10, 0},

{{{GEN_HEAD(2), GEN_HEAD(2), 0}}, 10, 0},

};

PyGC_Head *_PyGC_generation0 = GEN_HEAD(0);

一开始的所有容器对象都连接着0代的对象

编辑 | 码哥

图片源于网络,版权归原作者所有返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值