设为首页 加入收藏

TOP

Python垃圾回收(一)
2023-09-09 10:25:29 】 浏览:200
Tags:Python

Python版本

v3.9.17

分析代码的过程比较枯燥,可以直接跳转到总结。

只能被其他对象引用类型

比如:longobject、floatobject

floatobject

以floatobject为例子来分析,先看看结构定义

typedef struct {
    PyObject_HEAD
    double ob_fval;
} PyFloatObject;

// 展开PyObject_HEAD后
typedef struct {
    PyObject ob_base;
    double ob_fval;
} PyFloatObject;

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
} PyObject;

在PyObject中的_PyObject_HEAD_EXTRA,只有在编译时指定--with-trace-refs才有效,这里忽略即可。

./configure --with-trace-refs

可以看到在PyObject里有一个ob_refcnt的属性,这个就是引用计数。
当对引用计数减为0时,就会调用各类型对应的析构函数。

define Py_DECREF(op) _Py_DECREF(_PyObject_CAST(op))

void _Py_Dealloc(PyObject *op)
{
    destructor dealloc = Py_TYPE(op)->tp_dealloc;
    (*dealloc)(op);
}

static inline void _Py_DECREF(PyObject *op)
{
    if (--op->ob_refcnt != 0) {
    }
    else {
        _Py_Dealloc(op);
    }
}

能引用其他对象的类型

比如listobject,dictobject...

listobject

以listobject为例子来分析,先看看结构定义

typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

// 展开 PyObject_VAR_HEAD
typedef struct {
    PyVarObject ob_base;
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;

可以看出,PyObject_VAR_HEAD也就比PyObject_HEAD多了一个Py_ssize_t ob_size而已,这个属性是用来表示这个可变对象里元素数量。

因为可以引用其他对象,就有可能会出现环引用问题,这种问题如果再使用引用计数来作为GC就会出现问题。

lst1 = []
lst2 = []
lst1.append(lst2)
lst2.append(lst1)

当然这种情况可以使用弱引用,或者手动解除环引用。这些解决方案这里不深入,现在主要看看python是怎样应对这种情况。

对于这类型的对象在申请内存的时候调用的是PyObject_GC_New,而不可变类型是用PyObject_MALLOC。为了减少篇幅,删掉了一些判断逻辑。

typedef struct {
    // Pointer to next object in the list.
    // 0 means the object is not tracked
    uintptr_t _gc_next;

    // Pointer to previous object in the list.
    // Lowest two bits are used for flags documented later.
    uintptr_t _gc_prev;
} PyGC_Head;

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

static PyObject * _PyObject_GC_Alloc(int use_calloc, size_t basicsize)
{
    PyThreadState *tstate = _PyThreadState_GET();
    GCState *gcstate = &tstate->interp->gc;

    size_t size = sizeof(PyGC_Head) + basicsize;

    PyGC_Head *g;
    g = (PyGC_Head *)PyObject_Malloc(size);

    g->_gc_next = 0;
    g->_gc_prev = 0;
    gcstate->generations[0].count++; /* number of allocated GC objects */
    if (/* 判断是否可以执行GC */)
    {
        gcstate->collecting = 1;
        collect_generations(tstate);
        gcstate->collecting = 0;
    }
    PyObject *op = FROM_GC(g);
    return op;
}

在可变对象中,python又加上了一个PyGC_Head。通过这个PyGC_Head将listobject链接到gc列表中。

在分配完listobject内存后,紧接着调用_PyObject_GC_TRACK,链接到gc列表中。

static inline void _PyObject_GC_TRACK_impl(const char *filename, int lineno,
                                           PyObject *op)
{
    PyGC_Head *gc = _Py_AS_GC(op);

    PyThreadState *tstate = _PyThreadState_GET();
    PyGC_Head *generation0 = tstate->interp->gc.generation0;
    PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev);
    _PyGCHead_SET_NEXT(last, gc);
    _PyGCHead_SET_PREV(gc, last);
    _PyGCHead_SET_NEXT(gc, generation0);
    generation0->_gc_prev = (uintptr_t)gc;
}

通过这里的变量名,可以猜测使用到了分代垃圾回收。

分代回收

python手动执行垃圾回收一般调用gc.collect(generation=2)函数。

#define NUM_GENERATIONS 3

#define GC_COLLECT_METHODDEF    \
    {"collect", (PyCFunction)(void(*)(void))gc_collect, METH_FASTCALL|METH_KEYWORDS, gc_collect__doc__},

static PyObje
首页 上一页 1 2 3 4 5 6 下一页 尾页 1/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇【python技巧】替换文件中的某几行 下一篇Python名称空间和作用域,闭包函数

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目