电邮注册网站,企业宽带解决方案,百度竞价广告推广,医疗整形网站怎么做简介#xff1a; # Python GC机制 对于Python这种高级语言来说#xff0c;开发者不需要自己管理和维护内存。Python采用了引用计数机制为主#xff0c;标记-清除和分代收集两种机制为辅的垃圾回收机制。 首先#xff0c;需要搞清楚变量和对象的关系#xff1a; * 变量 # Python GC机制 对于Python这种高级语言来说开发者不需要自己管理和维护内存。Python采用了引用计数机制为主标记-清除和分代收集两种机制为辅的垃圾回收机制。 首先需要搞清楚变量和对象的关系 * 变量通过变量指针引用对象。变量指针指向具体对象的内存空间取对象的值。 * 对象类型已知每个对象都包含一个头部信息头部信息类型标识符和引用计数器
Python GC机制
对于Python这种高级语言来说开发者不需要自己管理和维护内存。Python采用了引用计数机制为主标记-清除和分代收集两种机制为辅的垃圾回收机制。
首先需要搞清楚变量和对象的关系
变量通过变量指针引用对象。变量指针指向具体对象的内存空间取对象的值。对象类型已知每个对象都包含一个头部信息头部信息类型标识符和引用计数器
引用计数
python里每一个东西都是对象它们的核心就是一个结构体PyObject其中ob_refcnt就是引用计数。当一个对象有新的引用时ob_refcnt就会增加当引用它的对象被删除ob_refcnt就会减少。当引用计数为0时该对象生命就结束了。
typedef struct_object {int ob_refcnt;struct_typeobject *ob_type;
} PyObject;#define Py_INCREF(op) ((op)-ob_refcnt) //增加计数
#define Py_DECREF(op) \ //减少计数if (--(op)-ob_refcnt ! 0) \; \else \__Py_Dealloc((PyObject *)(op))
可以使用sys.getrefcount()函数获取对象的引用计数需要注意的是使用时会比预期的引用次数多1原因是调用时会针对于查询的对象自动产生一个临时引用。
下面简单展现一下引用计数的变化过程。
一开始创建3个对象引用计数分别是1。之后将n1指向了新的对象JKL则之前的对象“ABC”的引用计数就变成0了。这时候Python的垃圾回收器开始工作将“ABC”释放。接着让n2引用n1。“DEF”不再被引用“JKL”因为被n1、n2同时引用所以引用计数变成了2。n1 ABCn2 DEFn3 GHIsys.getrefcount(n1)
2sys.getrefcount(n2)
2sys.getrefcount(n3)
2n1 JKLsys.getrefcount(n1)
2n2 n1sys.getrefcount(n1)
3sys.getrefcount(n2)
3sys.getrefcount(n3)
2
优缺点
优点实时性好。一旦没有引用内存就直接释放了。实时性还带来一个好处处理回收内存的时间分摊到了平时。
缺点维护引用计数消耗资源循环引用无法解决。
如下图典型的循环引用场景。对象除了被变量引用n1、n2外还被对方的prev或next指针引用造成了引用计数为2。之后n1、n2设成null之后引用计数仍然为1导致对象无法被回收。 标记-清除、分代收集
Python采用标记-清除策略来解决循环引用的问题。但是该机制会导致应用程序卡住为了减少程序暂停的时间又通过“分代回收”(Generational Collection)以空间换时间的方法提高垃圾回收效率。详见Python垃圾回收机制非常实用
Python C扩展的引用计数
Python提供了GC机制保证对象不被使用的时候会被释放掉开发者不需要过多关心内存管理的问题。但是当使用C扩展的时候就不这么简单了必须需要理解CPython的引用计数。
当使用C扩展使用Python时引用计数会随着PyObjects的创建自动加1但是当释放该PyObjects的时候我们需要显示的将PyObjects的引用计数减1否则会出现内存泄漏。
#include Python.hvoid print_hello_world(void) {PyObject *pObj NULL;pObj PyBytes_FromString(Hello world\n); /* Object creation, ref count 1. */PyObject_Print(pLast, stdout, 0);Py_DECREF(pObj); /* ref count becomes 0, object deallocated.* Miss this step and you have a memory leak. */
}
有亮点尤其需要注意
PyObjects引用计数为0后不能再访问。类似于C语言free后不能再访问对象。Py_INCREF、Py_DECREF必须成对出现。类似于C语言malloc、free的关系。
Python有三种引用形式分别为 “New”, “Stolen” 和“Borrowed” 引用。
New引用
通过Python C Api创建出的PyObject调用者对该PyObject具有完全的所有权。一般Python文档这样体现
PyObject* PyList_New(int len)Return value: New reference.Returns a new list of length len on success, or NULL on failure.
针对于New引用的PyObject有如下两种选择。否则就会出现内存泄漏。
使用完成后调用Py_DECREF将其释放掉。
void MyCode(arguments) {PyObject *pyo;...pyo Py_Something(args);...Py_DECREF(pyo);
}
将引用通过函数返回值等形式传递给上层调用函数但是接收者必须负责最终的Py_DECREF调用。
void MyCode(arguments) {PyObject *pyo;...pyo Py_Something(args);...return pyo;
}
使用样例
static PyObject *subtract_long(long a, long b) {PyObject *pA, *pB, *r;pA PyLong_FromLong(a); /* pA: New reference. */pB PyLong_FromLong(b); /* pB: New reference. */r PyNumber_Subtract(pA, pB); /* r: New reference. */Py_DECREF(pA); /* My responsibility to decref. */Py_DECREF(pB); /* My responsibility to decref. */return r; /* Callers responsibility to decref. */
}// 错误的例子a、b两个PyObject泄漏。
r PyNumber_Subtract(PyLong_FromLong(a), PyLong_FromLong(b));Stolen引用
当创建的PyObject传递给其他的容器例如PyTuple_SetItem、PyList_SetItem。
static PyObject *make_tuple(void) {PyObject *r;PyObject *v;r PyTuple_New(3); /* New reference. */v PyLong_FromLong(1L); /* New reference. *//* PyTuple_SetItem steals the new reference v. */PyTuple_SetItem(r, 0, v);/* This is fine. */v PyLong_FromLong(2L);PyTuple_SetItem(r, 1, v);/* More common pattern. */PyTuple_SetItem(r, 2, PyUnicode_FromString(three));return r; /* Callers responsibility to decref. */
}
但是需要注意PyDict_SetItem内部会引用计数加一。
Borrowed引用
Python文档中Borrowed引用的体现
PyObject* PyTuple_GetItem(PyObject *p, Py_ssize_t pos)
Return value: Borrowed reference.
Borrowed 引用的所有者不应该调用 Py_DECREF()使用Borrowed 引用在函数退出时不会出现内存泄露。。但是不要让一个对象处理未保护的状态Borrowed 引用如果对象处理未保护状态它随时可能会被销毁。
例如从一个 list 获取对象继续操作它但并不递增它的引用。PyList_GetItem 会返回一个 borrowed reference 所以 item 处于未保护状态。一些其他的操作可能会从 list 中将这个对象删除递减它的引用计数或者释放它导致 item 成为一个悬垂指针。
bug(PyObject *list) {PyObject *item PyList_GetItem(list, 0);PyList_SetItem(list, 1, PyInt_FromLong(0L));PyObject_Print(item, stdout, 0); /* BUG! */
}no_bug(PyObject *list) {PyObject *item PyList_GetItem(list, 0);Py_INCREF(item); /* Protect item. */PyList_SetItem(list, 1, PyInt_FromLong(0L));PyObject_Print(item, stdout, 0);Py_DECREF(item);
} 原文链接 本文为阿里云原创内容未经允许不得转载。