Python垃圾回收机制
1 引用计数:
Python采用引用计数的方式对对象进行引用管理,源码中Obj_ref变量为被引用次数,当有此对象的引用存在的时候,obj_ref-1, 反之+1,当obj_ref减为0时, 该对象立即被回收,对象占用的内存空间将被释放。引用计数机制所带来的维护引用计数的额外操作与Python运行中所进行的内存分配和释放,引用赋值次数是成正比的,这也是引用计数执行效率比较慢的原因。Python中使用很多内存池来进行不同对象的管理和优化。这里面还会存在循环引用的问题,当若干对象相互引用,会造成每个对象的引用计数都不会为0,这样这些对象所占用的内存永远不会被回收。
双向环状链表-Refchain:Python底层维护了一个双向循环链表RefChain,每当创建一个新对象的时候就会将对象添加到这个链表中。在refChain内部,每个对象都有一个ob_refcnt来记录当前对象被引用的次数
refChain中Object的定义
define PyObject_HEAD PyObject ob_base; #define PyObject_VAR_HEAD PyVarObject ob_base; // 宏定义,包含 上一个、下一个,用于构造双向链表用。(放到refchain链表中时,要用到) #define _PyObject_HEAD_EXTRA \ struct _object *_ob_next; \ struct _object *_ob_prev; typedef struct _object { _PyObject_HEAD_EXTRA // 用于构造双向链表 Py_ssize_t ob_refcnt; // 引用计数器 struct _typeobject *ob_type; // 数据类型 } PyObject; typedef struct { PyObject ob_base; // PyObject对象 Py_ssize_t ob_size; /* Number of items in variable part,即:元素个数 */ } PyVarObject;
C语言底层定义的两种结构,一种是PyObject 和PyVarObject。 当创建值类型数据的时候,创建PyObject(ob_next, ob_prev, ob_refcnt, type)4个值。 当创建像list,dict,tuple和set的时候,便创建PyVarObject(ob_next, ob_prev, ob_refcnt, type, ob_size)在值类型数据的基础上添加一个元素个数, 就是5个值。
问题
当你创建了list1, list2。list1中有一个引用时list2, list2中有一个引用是list1。这时候就会发生循环引用的问题,导致最后del两个list了但是他们的引用对象计数在refChain中还是1,永远不会被清楚,内存一直被占据着。
清除标记算法:
目的:为了解决引用计数器循环引用的不足。
实现:在python的底层再维护一个链表,链表中专门放哪些可能存在循环引用的对象(list, tuple, dict, set)
在python内部某种情况下触发,回去扫描可能存在循环应用的链表中的每个元素, 肩差是否有循环引用,如果有则让双方的计数器-1;如果是0则垃圾回收
使用此种方法无法解决循环引用的问题,所以引入标记清楚算法(Mark_Sweep)。GC会对所有对象打上标记,分为活动对象和非活动对象。Python中通过一个directed graph进行对所有对象的标记维护。沿着途中的所有引用,从root object遍历,能到达的对象为活动对象,无法到达的对象为非活动对象。GC首先会把这些标记为非活动对象的object回收
python 通过ref_count计算应用计数垃圾回收,gc_ref是ref_count副本,用于循环应用的垃圾回收,GC时分两步,一是gc_ref自减1,摘除循环引用;二是从root根出发标记是否可达。最后gc_ref为0且不可达将被回收
问题:
1.什么时候扫描
2.可能存在循环引用的链表扫描代价大, 每次扫描耗时久
生命代, 分代回收:
将可能存在循环应用的对象维护成3个链表:
0代: 0代中的对象个数达到700个扫描一次。
1代:0代扫描10次,则1代扫描一次
2代:1代扫描10次, 则2代扫描一次
Python中还会使用分代回收。Python将所有的对象的存活时间分为三个集合,0代(新代),1代(中年代),2代(老年代)。三个代分别指向3个不同的链表,对象的存活时间越长,GC回收对象的频率就越低。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾回收机制就会被触发,把那些可以被回收的对象回收掉, 那些不会回收的对象就会被一道中年代去。以此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期。当对象数量为700时进行一次年轻代回收,当进行10次年轻代回收后进行一次中年代回收。
Python缓存
池(int, str)
目的:为了避免重复创建和销毁一些常见对象,维护池 var < 257的时候,直接从缓存池中取int。 > 257的时候就会创建一个新的对象,开辟新的内存空间。
free List(float/list/tuple/dict)
当一个对象的引用计数器为0的时候,内部不会直接回收,二十直接将对西昂添加到free_list 链表中当缓存。当再去创建兑现过的时候,不会再重新开辟内存,而是直接使用free_list
常见类型的struct
float 类型
typedef struct { PyObject_HEAD double ob_fval; } PyFloatObject;
int类型
struct _longobject { PyObject_VAR_HEAD digit ob_digit[1]; }; /* Long (arbitrary precision) integer object interface */ typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */
str类型
typedef struct { PyObject_HEAD Py_ssize_t length; /* Number of code points in the string */ Py_hash_t hash; /* Hash value; -1 if not set */ struct { unsigned int interned:2; /* Character size: - PyUnicode_WCHAR_KIND (0): * character type = wchar_t (16 or 32 bits, depending on the platform) - PyUnicode_1BYTE_KIND (1): * character type = Py_UCS1 (8 bits, unsigned) * all characters are in the range U+0000-U+00FF (latin1) * if ascii is set, all characters are in the range U+0000-U+007F (ASCII), otherwise at least one character is in the range U+0080-U+00FF - PyUnicode_2BYTE_KIND (2): * character type = Py_UCS2 (16 bits, unsigned) * all characters are in the range U+0000-U+FFFF (BMP) * at least one character is in the range U+0100-U+FFFF - PyUnicode_4BYTE_KIND (4): * character type = Py_UCS4 (32 bits, unsigned) * all characters are in the range U+0000-U+10FFFF * at least one character is in the range U+10000-U+10FFFF */ unsigned int kind:3; unsigned int compact:1; unsigned int ascii:1; unsigned int ready:1; unsigned int :24; } state; wchar_t *wstr; /* wchar_t representation (null-terminated) */ } PyASCIIObject; typedef struct { PyASCIIObject _base; Py_ssize_t utf8_length; /* Number of bytes in utf8, excluding the * terminating \0. */ char *utf8; /* UTF-8 representation (null-terminated) */ Py_ssize_t wstr_length; /* Number of code points in wstr, possible * surrogates count as two code points. */ } PyCompactUnicodeObject; typedef struct { PyCompactUnicodeObject _base; union { void *any; Py_UCS1 *latin1; Py_UCS2 *ucs2; Py_UCS4 *ucs4; } data; /* Canonical, smallest-form Unicode buffer */ } PyUnicodeObject;
list类型
typedef struct { PyObject_VAR_HEAD PyObject **ob_item; Py_ssize_t allocated; } PyListObject;
tuple类型
typedef struct { PyObject_VAR_HEAD PyObject *ob_item[1]; } PyTupleObject;
list 类型
typedef struct { PyObject_HEAD Py_ssize_t ma_used; PyDictKeysObject *ma_keys; PyObject **ma_values; } PyDictObject;