一句话: Pthon的垃圾回收机制使用的是引用计数为主,清楚标记算法和分代为辅
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且不可达将被回收
问题:
- 什么时候扫描
- 可能存在循环引用的链表扫描代价大, 每次扫描耗时久
生命代, 分代回收:
将可能存在循环应用的对象维护成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;