Skip to content

Commit f0651b0

Browse files
committed
垃圾回收
垃圾回收
1 parent 24be694 commit f0651b0

7 files changed

Lines changed: 120 additions & 19 deletions

File tree

pic/python_advance/gc_1.png

25.4 KB
Loading

pic/python_basic/tuple3.png

19.4 KB
Loading
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Python垃圾回收机制
2+
**引用计数+标记清除+分代回收**
3+
## 1.通过引用计数来进行垃圾回收
4+
Python中每个对象内部都有一个引用计数器,程序在运行过程中会实时更新“引用计数”的值,当某个对象的引用计数值为0时,这个对象的内存就会被立即释放掉。**Python 使用标记清除(mark-sweep)算法和分代回收(generational),来启用针对循环引用的自动垃圾回收。**
5+
#### 增加引用计数的情况
6+
* 对象被创建的时候,a={}
7+
* 对象被引用的时候, b=a
8+
* 对象被作为参数传入到一个函数中func(a)
9+
* 对象作为元素存储在容器中
10+
* 可以通过sys.getrefcount()方法查看对象的引用计数值
11+
12+
#### 减小引用计数的情况
13+
* 对象别名被显示销毁 del
14+
* 对象别名被赋予新的对象
15+
* 一个对象离开他的作用域,函数执行结束对象引用减一
16+
* 对象所在的容器被销毁或者是从容器中删除对象
17+
18+
```python
19+
import sys
20+
21+
def func(a):
22+
pass
23+
24+
if __name__ == '__main__':
25+
a = [] # +1
26+
b = a # +1
27+
func(a) # 这里函数被调用的时候+1,函数执行结束引用计数-1
28+
dict_ = {'a': a} # +1
29+
print(sys.getrefcount(a)) # 使用getrefcount()方法本身也会+1,所以最终打印是4
30+
31+
b = None # -1
32+
dict_.clear() # -1
33+
print(sys.getrefcount(a)) # 4-1-1 = 2
34+
del a # del只是删除对象的别名a,并不删除对象本身,引用计数-1
35+
```
36+
37+
#### 引用计数的优点
38+
* 逻辑简单,效率高
39+
* 实时性,一旦一个对象的引用计数为0了,内存就直接释放了。(有的内存回收机制是有时间等待的)
40+
41+
#### 引用计数的缺点
42+
* 每个对象需要分配单独的空间统计引用计数,增加了内存负担
43+
* 当需要释放一个大的对象时,如字典,需要对引用的所有对象进行计算,花费时间比较长
44+
* 没办法处理循环引用的情况,必须要使用其它的垃圾回收算法对其进行补充
45+
46+
#### 循环引用
47+
a和b相互引用而再没有外部引用a与b中的任何一个,它们的引用计数都为1,但是如果按照引用计数法的话,永远不为0,永远无法回收。
48+
```python
49+
a = {'b': None}
50+
b = {'a': None}
51+
a['b'] = b
52+
b['a'] = a
53+
del a
54+
del b
55+
```
56+
57+
## 2.标记清除处理循环引用的情况
58+
59+
**只有容器才会产生循环引用的情况,如列表,字典,元组等,所以“标记清除”处理的也是容器循环引用的问题**
60+
* 标记阶段:遍历所有对象,如果是可达的,说明还有对象引用,就标记。
61+
* 清除阶段:再次遍历所有对象,如果说没有被标记,就将其回收
62+
63+
对象之间的引用通过指针连接在一起,构成一个有向图,每个对象是有向图的节点,引用关系构成有向图的变,从根对象出发,沿着有向边遍历对象,可以到达的标记为活动对象,不可到达的就是要被清除的对象。根节点就是全局变量、调用栈、寄存器。
64+
65+
如图所示,因为执行了 del a, del b,a和b都是互相引用,没有别的外部引用指向a和b,所以从根节点遍历无法到达ab节点,因此循环引用的ab对象会被回收。
66+
67+
![](../pic/python_advance/gc_1.png)
68+
69+
## 3.分代回收
70+
* 循环引用对象的回收过程中,程序是暂停的,所以应该控制“标记清除”的频次,而且不可能每次“标记清除”都遍历所有的对象。
71+
* 分代回收用空间换取时间
72+
* Python将所有对象分为三代,刚创建的对象是第一代,经过一次垃圾回收仍然存在的对象会放入到第二代,第二代对象经过一次垃圾回收会放入到第三代
73+
* 当垃圾回收器中新增的对象减去要删除的对象到达一定阈值时,就执行一次垃圾回收
74+
```python
75+
import gc
76+
print(gc.get_threshold()) # (700, 10, 10)
77+
# 700:当垃圾回收器中新增的对象减去释放的对象差值大于700时,就执行一次一代回收
78+
# 10次一代回收执行一次二代回收
79+
# 10次二代回收执行一次三代回收
80+
# 所以越后代的对象说明越重要,越不容易被回收
81+
```
82+
83+
**在程序中,大部分(80%-90%)对象的生存周期比较短,只有少量对象生命周期比较长,甚至伴随着整个程序,所以,对象存在的时间越长,越不可能是垃圾,“标记清除”越应该减少去遍历这类对象,所以应该将这类对象放入到第二代第三代,以此来提高垃圾回收的效率。**

python_advance/main.md

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
## Python进阶
2-
1. [Python中的下划线](Python中的下划线.md)
2+
* [Python中的下划线](Python中的下划线.md)
33

4-
2. [可迭代对象、迭代器、生成器](可迭代对象迭代器生成器.md)
5-
3. [位置参数、默认参数、可变参数、关键字参数](位置参数默认参数可变参数关键字参数.md)
6-
4. [模块和包](模块和包.md)
7-
5. [通用日志模块](通用日志模块.md)
8-
6. [变量的作用域与闭包](变量的作用域.md)
9-
7. [装饰器](装饰器.md)
10-
8. [进程与线程概念](进程与线程概念.md)
11-
9. [Python中的多线程](Python中的多线程.md)
12-
10. [Python中的多进程](Python中的多进程.md)
13-
11. [socket网络编程](socket网络编程.md)
14-
12. [Python实现TFTP文件传输](Python实现TFTP文件传输.md)
15-
13. [Python连接数据库](Python连接数据库.md)
16-
14. [Git基础](Git基础.md)
17-
15. [Python之AES加密解密](Python之AES加密解密.md)
18-
16. [MongoDB存储图片](MongoDB存储图片.md)
19-
17. [python中的协程](python中的协程.md)
20-
18. [concurrent.futures模块](concurrentfutures模块.md)
4+
* [可迭代对象、迭代器、生成器](可迭代对象迭代器生成器.md)
5+
* [位置参数、默认参数、可变参数、关键字参数](位置参数默认参数可变参数关键字参数.md)
6+
* [模块和包](模块和包.md)
7+
* [通用日志模块](通用日志模块.md)
8+
* [变量的作用域与闭包](变量的作用域.md)
9+
* [装饰器](装饰器.md)
10+
* [进程与线程概念](进程与线程概念.md)
11+
* [Python中的多线程](Python中的多线程.md)
12+
* [Python中的多进程](Python中的多进程.md)
13+
* [socket网络编程](socket网络编程.md)
14+
* [Python实现TFTP文件传输](Python实现TFTP文件传输.md)
15+
* [Python连接数据库](Python连接数据库.md)
16+
* [Git基础](Git基础.md)
17+
* [Python之AES加密解密](Python之AES加密解密.md)
18+
* [MongoDB存储图片](MongoDB存储图片.md)
19+
* [python中的协程](python中的协程.md)
20+
* [concurrent.futures模块](concurrentfutures模块.md)
21+
* [Python垃圾回收机制](Python垃圾回收机制.md)

python_basic/main.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@
2121
19. [深拷贝与浅拷贝](深拷贝与浅拷贝.md)
2222
20. [列表推导式,字典推导式,生成器表达式,map,filter,reduce](列表推导式.md)
2323
21. [== 与 is的区别](is.md)
24+
25+
* [总结](总结.md)

python_basic/关于tuple.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
1-
#### 不要把可变对象放在元组中
1+
**元组不可变指的是tuple数据结构中的物理内容不变,也就是元组保存的引用是不可变的。**
2+
3+
**所以如果元组引用的元素是可变的,那即使元组本身包含的引用不可变,元素依然可变**
4+
```python
5+
# 元组内保存的三个元素的内存地址是不可变的
6+
t1 = (1, 2, [30, 40])
7+
print(id(t1[-1])) # 1742619956424
8+
t1[-1].append(50)
9+
print(t1) # (1, 2, [30, 40, 50])
10+
print(id(t1[-1])) # 1742619956424
11+
```
12+
13+
![](../pic/python_basic/tuple3.png)
14+
15+
#### 尽量不要把可变对象放在元组中
216
#### 增量赋值不是一个原子操作,虽然程序抛出异常,但还是会添加成功
17+
+= 是赋值操作,相当于要改变引用地址,元组内引用地址是不可变的,所以会报错
318
答案:D
419
![](../pic/python_basic/tuple2.png)

python_basic/总结.md

Whitespace-only changes.

0 commit comments

Comments
 (0)