一、引用机制
- python中每个对象都维护一个引用计数 ob_ref 字段
- 当有新的引用指向该对象的时候,引用计数+1
- 当有无效引用发生的时候,引用计数-1
- 当引用计数为0时,销毁对象
引用机制的优点:
- 简单
- 实时性
引用机制的缺点:
- 维护引用计数消耗资源
- 循环引用时,无法回收
>>> from sys import getrefcount
>>> a = 1000
>>> getrefcount(a)
2
只引用赋值给了a,但是引用计数为2是因为=>在使用getrefcount的时候也会有一次临时引用,
获取到a的时候这个临时引用就销毁了
>>> b = a
>>> getrefcount(a)
3
>>> c = []
>>> c.append(b)
>>> getrefcount(a)
4
getrefcount => 得到的引用计数会比实际多1次
- 循环引用
- 根据引用计数的规律,出现循环引用的情况=> 引用计数不为0 => 无法通过引用计数来释放内存
=> 会造成内存泄漏- 内存泄漏:有一部分内存被占用无法释放,进程也无法访问
内存泄漏可能造成内存溢出 - 内存溢出(oom => out of memory):内存不够,程序需要的内存大于系统空闲内存
- 内存泄漏:有一部分内存被占用无法释放,进程也无法访问
- 根据引用计数的规律,出现循环引用的情况=> 引用计数不为0 => 无法通过引用计数来释放内存
>>> x = [2]
>>> y = [3]
>>> x.append(y)
>>> y.append(x)
>>> getrefcount(x)
3
>>> getrefcount(y)
3
>>> del x
>>> del y
二、垃圾回收
-
回收原则:当python的某个对象的引用计数降为0时,可以被垃圾回收
-
gc(garbage collection)机制
- 分代回收
- 标记清除
>>> x = [2]
>>> y = [3]
>>> x.append(y)
>>> y.append(x)
>>> del x
>>> del y
>>> gc.collect()
2
1. 分代回收
启动垃圾回收的时候确定哪些是扫描对象的
2. 标记清除
主要解决循环引用 => 清除非活动的标记
三、内存池
- 预先创建好一个小整型数池 [-5,256]
>>> a = 3
>>> getrefcount(a)
55
>>> a = 1
>>> getrefcount(a)
874
>>> a = 300
>>> getrefcount(a)
2
>>>
在这个小整型池里的数就不会再重复创建了,直接引用池子里已经创建好的
- 字符串驻留区
- 单个字符创建之后都会放在驻留区
- 多个字符创建之后如果包含特殊字符就不会放在字符串驻留区,如果没有特殊字符就会存放到驻留区
>>> str1 = "abcxyz"
>>> str2 = "abcxyz"
>>> id(str1)
139668314459528
>>> id(str2)
139668314459528
>>> str1 = "xyz abc"
>>> str2 = "xyz abc"
>>> id(str1)
139668313995952
>>> id(str2)
139668313995896
总结
python的内存管理:
以引用计数为主,
分代回收、标记清除为辅的垃圾回收方式。
以及对小整数型进行缓存和简单字符串驻留的内存池机制