python内存管理之垃圾回收机制

python内存管理之垃圾回收机制

前戏部分

# python中定义一个变量,如:`name = 'the3times'`,在计算机底层会发生这样一件事:操作系统调用硬件,在内存中开辟一块空间,将值'the3times'存放在这块内存空间中;然后将变量名name与这块内存空间的地址绑定关联在一起;程序通过变量名name的调用来唯一访问值'the3times'。

# 这样的话,如果程序中有许多变量需要定义就会在内存中开辟大量的内存空间;
# 另外,我们知道内存是有限的,变量数量如果太多就会造成内存不足甚至内存溢出的风险。
# 从这个角度出发,程序开发必须时刻且谨慎注意内存管理问题。然而内存管理这件事是繁琐且存在分险的。

栈区 & 堆区

我们知道变量的定义会把变量值存储在内存中。其实,具体的是把变量值存放在内存的堆区中,把变量名和变量值的绑定关系存放在栈区。绑定关系就是变量名保存变量值所在的内存地址。

通过访问变量名,找到其对应存储的变量值的内存地址,进而访问到变量值。

x = 10
y = 20

引用小猿取经的图片

直接引用 & 间接应用

通过栈区的变量名直接找到堆区的变量值,这种引用(访问)就是直接引用。

通过从栈区出发引用到堆区后,再进一步引用才能找到变量值的内存地址,这种是间接引用。

l2 = [20, 30]  # 列表本身被变量名l2直接引用,包含的元素被列表间接引用
x = 10  # 值10被变量名x直接引用
l1 = [x, l2]  # 列表本身被变量名l1直接引用,包含的元素被列表间接引用

引用小猿取经的图片

垃圾回收机制

幸运的是,python解释器帮我们做了这件事。python解释器自带的内存管理:垃圾回收机制。垃圾回收机制用来回收内存中不再使用的变量值所占用的内存空间

垃圾回收机制原理

python垃圾回收机制主要使用 引用计数 的方式来跟踪和回收垃圾。在引用计数的基础上为了解决容器型数据产生的相互引用问题,又提出 标记-清除 的机制, 并且通过 分代回收 以空间换取时间的方式来进一步提高垃圾回收的效率。

引用计数

# 引用计数原理

# 引用计数指的是,当一个内存地址内通过直接或间接的方式与一个变量绑定关系时,则将该内存地址上的值的引用计数增加一个;当该内存地址上的值与一个变量名解除关系时,则该值的引用计数减少一个。当该值的应用技术等于0的时候,表示该值和任何变量名都没有绑定关系,即该值是无用的,是垃圾。次吃python解释器就会回收该值所在内存空间的地址。

# 直接引用指得是通过: 变量名=变量值 的方式绑定引用关系
# 间接引用指的是通过:容器型数据产生的绑定关系, 如: 
l1=[]
l2=[]
l1.append(l2)
l2.append(l1) 

# del 变量名 解除直接引用的绑定关系,间接引用的绑定关系不会解除

标记回收

# 引用计数非常棒的解决了垃圾回收问题,但是它却又一个致命的bug
# 当在容器型数据中存在相关引用的绑定关系时,如果通过del解除了变量的绑定关系,只是解除了直接引用关系,间接的相互引用关系时依然存在在内存中。此时该内存上的值是不能访问到的,应该被回收,但是此时的按照引用计数的原理,该值的引用计数不为0,python垃圾回收机制不会回收这快内存地址。这是十分危险的

# 为了解决这个相互引用的bug,python解释器提供了‘标记-清除’的方法来回收垃圾。

# ‘标记-清除’ 原理简介:
# 在内存中存在两个区,栈区和堆区
# 变量名和变量值的绑定关系保存在栈区;变量值保存在堆区。垃圾回收机制回收的是堆区的内存空间

# 当python解释器发现内存不够使用时,会启动‘标记-清除’机制
# 先标记:扫面栈区的所有变量名找到与之直接绑定或间接绑定的所有堆区的内存地址,将这些内存地址标记为存活;此时堆区剩下的所有其他内存地址是没有与任何变量名绑定关系的,标记为失活。
# 再清除:标记结束后,启动清除机制,python解释器会将堆区的所有标记为失活的内存地址全部回收。

# 通过’标记-清除‘的方式,就完美的解决了相互引用的bug

分代-回收

# 基于引用计数的方式回收内存,每次回收内存都需要把所有的对象的引用计数遍历一遍,这是非常低效率的。为了优化内存回收的性能,python解释器在引用计数的基础上引入'分代-回收'

# '分代回收'是将所有变量分成不同的等级,每个等级在内存回收时的扫描频率是不同的。
# 对于新定义的变量,它的回收频率较高,一段时间后,那些还在使用的变量将升级到较低扫描频率的等级里
# 依次类推,总的原则就是对于那些经常使用的变量,它的扫描频率降低

# 但是它依然是不够完美的,它将导致某些情况下某些变量的回收时间被延长。
# 空间换时间:没有及时回收那些扫描频率底但是早已不再使用的内存空间。
# 通过'分代-回收'机制很好的优化了python解释器垃圾回收的效率,通过牺牲极小部分垃圾不会被及时回收为代价,保证'引用计数'整体上的性能提升。
posted @ 2020-03-05 17:27  the3times  阅读(574)  评论(0编辑  收藏  举报