Loading

Python垃圾回收机制

垃圾回收机制

垃圾回收机制(Garbage Collection),简称GC,是Python解释器自带的机制,专门用来进行垃圾回收。

在定义一个变量时,会申请内存空间,当该变量使用完毕,也应该释放掉该变量所占用的内存空间,在C中需要程序员手动释放掉内存,而Python则由GC机制进行回收。

无论何种垃圾回收机制,一般都分为两个阶段:垃圾检测和垃圾回收。垃圾检测就是区分已分配内存中的“可回收”和“不可回收”内存。垃圾回收则是使操作系统重新掌握垃圾检测阶段所标识出来的“可回收”内存块。

所谓垃圾回收,并不是直接把这块内存的数据直接清空了,而是将使用权重新交给了操作系统,不会应用程序霸占了。

什么是垃圾

  1. 当一个变量调用完毕,且后续不再需要时,便是垃圾。
  2. 当指向该变量地址的变量名指向另一个地址时,原变量内存地址无法被访问,此时该变量也是垃圾。

Python定义变量原理

在Python中一切皆对象,内存空间分为栈区和堆区,其中:

栈区:

  • 保存变量名与变量值的内存地址的关联关系

堆区:

  • 保存实际的变量值,也是内存管理回收的地方

GC机制原理

  1. 引用计数
  2. 分代回收
  3. 标记清除

引用计数

引用计数原理是每个对象维护一个ob_refcnt,用来记录当前对象被引用的次数。该方式的优点是:一旦引用计数为0,则会直接被回收。

引用计数加1的情况:

  • 对象被创建:x=10
  • 对象被引用:y=x
  • 对象被当做参数传入:func(x)
  • 对象作为容器类型的元素

引用计数减1的情况:

  • 显示销毁对象的引用:del x
  • 对象的引用指向其他对象:x = 20
  • 对象的引用离开了它的作用域,比如函数的局部变量,在函数执行完毕时,也会被销毁(除非取栈帧)
  • 对象的引用所在的容器被销毁,或者从容器中删除等

查看引用计数:

使用sys.getrefcount(obj),但是由于对象会作为参数传进去,所以引用计数会+1。

直接引与间接引用:

直接引用:从栈区直接指向到实际值的引用。

间接引用:从栈区指向堆区后,再次引用才能到达实际存放值的内存,容器类型。

引用计数的缺陷:

循环引用:当两个容器类型对象,内部分别将对方添加为元素,此时即便删除了变量名,这两个对象的引用计数容不能为0。

l1 = []
l2 = []
l1.append(l2)
l2.append(l1)
del l1,l2

此时虽然没有通过栈区指向堆区,但它们彼此指向,导致引用计数不为0,如果程序一直运行的话,是有可能发生内存泄露的。

标记清除

标记清除就是为了解决“循环引用”的问题。其过程为

  1. 寻找根对象(root object)的集合,root object就是一些全局引用和函数栈的引用。
  2. 遍历root object集合,对每一个引用可以直接或间接访问到的对象标记为存活的对象,其余均为非存活对象,应该被清除。
  3. 遍历堆中的所有对象,对未被标记的对象全部清除掉。

分代回收

基于引用计数的回收机制,每次进行内存回收,都需要把对象的引用计数遍历一遍,这是非常耗时的,于是就引入了分代回收来提升效率,分代回收采用的是“空间换时间”策略。

分代

在经历多次扫描的情况下,都没有被回收的对象,那么就认为,该变量是常用变量,对其的扫描频率会降低。具体的原理为:

分代指的是根据存活时间来为变量划分不同等级,也就是不同的代,垃圾回收的扫描频率会随着“代”的存活时间增大而减小。

“代”可以想象成链表,属于同一个代的对象都被连接在同一个链表中。在Python中总共存在三条链表,说明所有的对象可以分为三代:零代、一代、二代。一个“代”就是一条可收集对象链表。

第0代链表最多可容纳700个对象,一旦超过700这个阈值,那么会立即出发垃圾回收机制。

1代链表和2代链表触发垃圾回收的条件又是什么呢?当0代链表触发了10次垃圾回收的时候,会触发一次1代链表的垃圾回收。当1代链表触发了10次垃圾回收的时候,会触发一次2代链表的垃圾回收。

  • 在清理1代链表的时候,会顺带清理0代链表
  • 在清理2代链表的时候,会顺带清理0代链表和1代链表

gc模块

gc模块底层就是gcmodule,该模块是用C写的,当python编译好时,就内嵌在解释器里面了。我们可以导入它,但是在Python安装目录上看不到。

gc.enable:开启垃圾回收

这个函数表示开启垃圾回收机制,默认是自动开启的。

gc.disable:关闭垃圾回收

import gc
	
# 关掉gc
gc.disable()

# 开启
gc.enable()
posted @ 2021-06-19 21:50  吃了好多肉  阅读(499)  评论(0编辑  收藏  举报