若是对象的引用数量增长,就在该对象的计数器上进行增量操做。在实际中它是由宏Py_INCREF() 执行的。编程
#define Py_INCREF(op) (((PyObject*)(op))->ob_refcnt++) #define Py_XINCREF(op) if ((op) == NULL) ; else Py_INCREF(op)
除了增量操做外,还要执行NULL检查,Py_XINCREF(op)。函数
Include/object.hdebug
typedef ssize_t Py_ssize_t;
ssize_t型,在32位环境下是int在64位下是long,它的大小由系统决定。这里定义的计数器它是能够为负数的,那么就有问题了,计数器是有符号整数,他能表达的最大数仅仅是无符号整数的一半。这样不会内存溢出吗?咱们以前说对象是4字节对齐的,既然是按4字节对齐,咱们就能够获得这样分下来,即便全部对象都指向某一个对象,也是不会溢出的。设计
那么,负的计数器表达的是什么?在debug中,会存在减数操做过分,和增量操做遗失的状况。负的计数器就是为它而设计的。在debug中,Py NegativeRefcount() 函数会把变为负数的对象信息当成错误信息输出。指针
其中成员 tp_dealloc 存着负责释放各个对象的函数指针,好比下面这个释放元组对象的函数指针。code
Objects/tupleobject.c对象
static void tupledealloc(register PyTupleObject *op) { register Py_ssize_t i; register Py_ssize_t len = Py_SIZE(op); if (len > 0) { i = len; /* 将元组内的元素进行减量 */ while (--i >= 0) Py_XDECREF(op->ob_item[i]); } /* 释放元组对象 */ Py_TYPE(op)->tp_free((PyObject *)op); Py_TRASHCAN_SAFE_END(op) }
PyObject_GC_Del()blog
void PyObject_GC_Del(void *op) { PyGC_Head *g = AS_GC(op); /* 省略部分:释放前的处理 */ PyObject_FREE(g); }
这里的 PyObject_FREE(),就是上一节中的 PyObject_Free()函数,这个函数会对对象进行释放。不过我是怎么知道的呢。此处又有宏定义。#define PyObject_FREE PyObject_Free。位于Include/objimpl.h内存
元组减量操做以下图示:
就是咱们类里常常写的 __del__
终结器指的是与对象的释放处理挂钩的一个功能。列表和字典等内置对象基本上是不能设置终结器的,能定义终结器的只有用户建立的类。
# 一个终结器 class Foo(object): def __def__(self): # 定义终结器 print("GKD")
这种状况下,当Foo被释放的时候,就会输出GKD。
那么Foo实例其实是怎么调用的呢?以下示:
Objects/typeobject.c:subtype_dealloc():单独拿出终结器的部分
static void subtype_dealloc(PyObject *self) { PyTypeObject *type, *base; destructor basedealloc; type = Py_TYPE(self); if (type->tp_del) { _PyObject_GC_TRACK(self); type->tp_del(self); } /* 省略 */ }
实例的状况下,变量 tp_del 中保存着执行终结器所需的 slot_tp_del() 函数
Objects/typeobject.c:slot_tp_del()
static void slot_tp_del(PyObject *self) { static PyObject *del_str = NULL; PyObject *del, *res; self->ob_refcnt = 1; /* 若是有__del__就执行它 */ del = lookup_maybe(self, "__del__", &del_str); if (del != NULL) { res = PyEval_CallObject(del, NULL); /* 省略部分:错误检查和后处理等 */ } if (--self->ob_refcnt == 0) return; /* 退出函数 */ /* 省略部分:最终化时有引用的状况下的应对处理 */ }
先用lookup_maybe(),取出实例中的__del__,而后使用 PyEval_CallObject()来执行它。
在python中,正常状况是要对对象的计数器进行增量和减量操做的。可是并非全部地方都须要这样作。
好比说在python中编写c的扩展模块:当从局部变量引用某个对象,大多数状况下是能够不执行计数处理的,由于从局部来讲,咱们引用它以后给计数器增量,退出后局部后又要减量。这实际上没有任何意义。不过也能够这样作。
原本计数器的做用是告诉GC这个对象被引用了,不要回收。那若是计数器的值已是大于0了。咱们还须要这样的增量计数器吗?增量以后计数器局部使用完后仍是会被减量的。
可是在局部变量的做用域中,若是对象的计数器为0那就必需要进行增量操做对变量进行保护了。
像这样的状况,什么时候对对象的计数器增量,什么时候减量,彻底能够有编程人员本身判断,若是不能判断则就按照规则来。