python中对于对象内存管理有两种方法,引用计数/GC 。
引用计数策略应用到每一个对象的管理中,接收/返回对象都须要+-对象的计数,而对象是否支持GC则是可选的,由于GC的存在是为了解决引用计数留下的循环引用问题,对于没有包含其余对象指针的对象能够不支持GC。python
引用计数的优点在于简单,把对象销毁时间分摊到程序生命周期程序员
static PyObject* PyPerson_New(PyTypeObject *type, PyObject *args, PyObject *kwds){ PyObject *ret = create_person(...); //发生异常 销毁对象 if(PyErr_Occurred()){ Py_XDECREF(ret); return NULL; } if(ret == NULL) FAST_RETURN_ERROR("create person obj fail"); if(!check_person(ret)){ Py_XDECREF(ret); //销毁对象 -1 Py_XINCREF(Py_None); //返回对象 +1 return Py_None; } return ret; }
上面的实例代码演示了返回对象计数+1 和 对象超出做用于计数-1
当对象计数==0的时候 调用typeobject的tp_dealloc函数完成对象清理jvm
#define Py_DECREF(op) \ do { \ PyObject *_py_decref_tmp = (PyObject *)(op); \ if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \ --(_py_decref_tmp)->ob_refcnt != 0) \ _Py_CHECK_REFCNT(_py_decref_tmp) \ else \ _Py_Dealloc(_py_decref_tmp); \ } while (0)
固然引用计数也有众所周知的缺点,循环引用,因此仍是须要引入GC机制来弥补函数
python gc使用的策略是标记-清除,根据是否从root object可达判断一个对象是不是垃圾对象ui
因为是否支持GC是可选的,因此咱们要主动选择对象是否支持GC,只须要在typeobject中加入一个标记就好 Py_TPFLAGS_HAVE_GC
debug
gc对象内除了对象自己的数据,还增长了一些gc信息,具体能够看看gc对象内存分配过程:设计
PyObject * PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems) { //...... if (PyType_IS_GC(type)) obj = _PyObject_GC_Malloc(size); else obj = (PyObject *)PyObject_MALLOC(size); //..... } static PyObject * _PyObject_GC_Alloc(int use_calloc, size_t basicsize) { PyObject *op; PyGC_Head *g; size_t size; if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) return PyErr_NoMemory(); size = sizeof(PyGC_Head) + basicsize; //.... }
能够看出gc对象内存模型以下:指针
————----- |gc head| |-------| | obj | | | ————-----
//经过PyGC_Head把gc对象造成链表 typedef union _gc_head { struct { union _gc_head *gc_next; union _gc_head *gc_prev; Py_ssize_t gc_refs; //gc对象的状态 } gc; double dummy; /* force worst-case alignment */ } PyGC_Head;
/设置为untrack 未追踪 g->gc.gc_refs = 0; _PyGCHead_SET_REFS(g, GC_UNTRACKED); //把新对象统计在第0 代 _PyRuntime.gc.generations[0].count++; /* number of allocated GC objects */ //若是第0代对象数超过了阈值 触发gc if (_PyRuntime.gc.generations[0].count > _PyRuntime.gc.generations[0].threshold && _PyRuntime.gc.enabled && _PyRuntime.gc.generations[0].threshold && !_PyRuntime.gc.collecting && !PyErr_Occurred()) { _PyRuntime.gc.collecting = 1; collect_generations(); //gc _PyRuntime.gc.collecting = 0; }
python gc也是分代,一样具备新生代、老年代的表现形式,和jvm gcheap 分代不一样的是 这里的代只是统计意义不具有内存占用code
注意到走完对象内存分配的流程,对象其实尚未真正的分配到某一代中
在PyObject_GC_Alloc中分配完内存以后才会执行这一步对象
if (PyType_IS_GC(type)) _PyObject_GC_TRACK(obj); //加入到对象链表 把对象加入到第0代的对象链表 #define _PyObject_GC_TRACK(o) do { \ PyGC_Head *g = _Py_AS_GC(o); \ if (_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED) \ Py_FatalError("GC object already tracked"); \ _PyGCHead_SET_REFS(g, _PyGC_REFS_REACHABLE); \ 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);
要筛选出垃圾对象,最直接的方法就是从rootobject开始把全部能访问到的对象都标记上,剩下的就是垃圾对象了。这个过程须要作两个事,1.肯定哪些是rootobject 2.从rootobject遍历对象。
static Py_ssize_t collect_generations(void) { int i; Py_ssize_t n = 0; //查找最老的 超出阈值的代 for (i = NUM_GENERATIONS-1; i >= 0; i--) { if (_PyRuntime.gc.generations[i].count > _PyRuntime.gc.generations[i].threshold) { //若是long_lived对象不是不少 则避免full gc if (i == NUM_GENERATIONS - 1 && _PyRuntime.gc.long_lived_pending < _PyRuntime.gc.long_lived_total / 4) continue; n = collect_with_callback(i); //收集 gen[i] - gen[0] break; } } return n; }
在肯定rootobject以前,咱们要先解决对象遍历的问题。由于咱们须要从一个对象开始访问它引用的对象,也就是广度优先遍历,因此不能直接遍历gc对象链表,而是使用额外的机制。
static void subtract_refs(PyGC_Head *containers) { traverseproc traverse; PyGC_Head *gc = containers->gc.gc_next; for (; gc != containers; gc=gc->gc.gc_next) { traverse = Py_TYPE(FROM_GC(gc))->tp_traverse; (void) traverse(FROM_GC(gc), (visitproc)visit_decref, NULL); } }
这个遍历机制就是typeobject中的tp_traverse函数,在tp_traverse函数中对象必须把引用到的对象交给函数visitproc处理,这样就完成了对象的广度优先遍历。
static int person_traverse(PyObject *self, visitproc visit, void *arg){ Person *p = (Person*)self; //visit(p->dict,arg) Py_VISIT(p->dict); return 0; }
全部对象和对象直接的引用造成了一个有向图,先把对象之间的引用去掉,那么最后计数>0的代表对象存在非对象间引用 也就是rootobject
接回上面的例子,visit_decref就是用来把对象中的引用-1的函数
static int visit_decref(PyObject *op, void *data) { if (PyObject_IS_GC(op)) { PyGC_Head *gc = AS_GC(op); if (_PyGCHead_REFS(gc) > 0) _PyGCHead_DECREF(gc); } return 0; }
完成第一轮筛选后,把计数>0的标记未reachable,计数==0的标记为unreachable
static void move_unreachable(PyGC_Head *young, PyGC_Head *unreachable) { PyGC_Head *gc = young->gc.gc_next; while (gc != young) { PyGC_Head *next; //refs > 0 通过上面的refs-1 root object refs>0 if (_PyGCHead_REFS(gc)) { PyObject *op = FROM_GC(gc); traverseproc traverse = Py_TYPE(op)->tp_traverse; assert(_PyGCHead_REFS(gc) > 0); //设置对象为reachable _PyGCHead_SET_REFS(gc, GC_REACHABLE); //从这个rootobject 能访问到的对象都是 reachable (void) traverse(op, (visitproc)visit_reachable, (void *)young); next = gc->gc.gc_next; if (PyTuple_CheckExact(op)) { _PyTuple_MaybeUntrack(op); } } else { //unreachable 这里会误判 遍历的时候会修正 next = gc->gc.gc_next; gc_list_move(gc, unreachable); _PyGCHead_SET_REFS(gc, GC_TENTATIVELY_UNREACHABLE); } gc = next; } } static int visit_reachable(PyObject *op, PyGC_Head *reachable) { if (PyObject_IS_GC(op)) { PyGC_Head *gc = AS_GC(op); const Py_ssize_t gc_refs = _PyGCHead_REFS(gc); if (gc_refs == 0) { //计数+1 这样等下遍历到它就会归为 reachable _PyGCHead_SET_REFS(gc, 1); } else if (gc_refs == GC_TENTATIVELY_UNREACHABLE) { //上面遍历的时候误判了 把对象放回reachable链表 gc_list_move(gc, reachable); _PyGCHead_SET_REFS(gc, 1); } } return 0; }
完成了reachable对象和unreachable对象筛选后,存活对象须要移动到老年代中
if (young != old) { //若是是gen[1] 存活数到统计起来 这个会影响到full gc if (generation == NUM_GENERATIONS - 2) { _PyRuntime.gc.long_lived_pending += gc_list_size(young); } //存活对象进入到更老的gen gc_list_merge(young, old); } else { //若是是full gc 会untrack掉dict对象减轻gc负担 untrack_dicts(young); _PyRuntime.gc.long_lived_pending = 0; //对象进入long lived状态 _PyRuntime.gc.long_lived_total = gc_list_size(young); }
距离真正完成对象筛选仍是差最后一步,由于设计遗留问题若是对象实现了tp_del函数 会有一些麻烦。由于有的对象会在tp_dealloc、tp_free中调用引用对象的tp_del作清理,可是gc并不能保证A引用B,B必定比A销毁晚,若是B销毁了,A还调用B的tp_del会致使内存错误,因此实现了tp_del的对象会被放弃收集。为了让程序员有机会手动去清理这部分对象,gc会把这部分对象存放到garbage链表中。
gc_list_init(&finalizers); //实现了tp_del的对象移动到finalizers链表 move_legacy_finalizers(&unreachable, &finalizers); //设置为reachable move_legacy_finalizer_reachable(&finalizers); //存放到 garbage 链表 让程序员本身处理 handle_legacy_finalizers(&finalizers, old);
static void delete_garbage(PyGC_Head *collectable, PyGC_Head *old) { inquiry clear; while (!gc_list_is_empty(collectable)) { PyGC_Head *gc = collectable->gc.gc_next; PyObject *op = FROM_GC(gc); //定义了DEBUG_SAVEALL会致使不清除 而是存放到grabage链表 if (_PyRuntime.gc.debug & DEBUG_SAVEALL) { PyList_Append(_PyRuntime.gc.garbage, op); } else { if ((clear = Py_TYPE(op)->tp_clear) != NULL) { //调用tp_clear Py_INCREF(op); clear(op); Py_DECREF(op); } }
tp_clear要作的就是引用对象计数-1,把对象从unreachable移除,释放对象内存回内存池
static int person_clear(PyObject *self){ Person *p = (Person*)self; Py_CLEAR(p->dict); //PyObject_GC_Del Py_TYPE(self)->tp_free(self); return 0; }