垃圾回收机制
说白了就是十二个字
引用计数、标记清除、分代回收
看不懂没关系我们往下看:
【1】引用计数
Python 语言默认采用的是 引用计数 的垃圾回收机制。 该算法的原理是:每个对象维护一个 ob_ref
的字段,用来记录该对象当前被引用的次数,每当有新的引用指向该对象是,它的引用计数 ob_ref
+ 1,每当该对象的引用失效时,计数器 ob_ref - 1,一旦引用计数为 0,该对象立即被回收,对象占用的空间会被释放。它的缺点是需要额外的空间维护引用计数,这个问题是其次的,最主要的问题是它不能解决对象中的 "循环引用" 问题,因此,很多语言比如 java 并没有采用才算法来做垃圾的收集机制。
什么是循环-引用?A 和 B 相互引用而在外部没有引用 A B 中的任何一个,它们的引用计数虽然都为 1,但显然应该被回收:
a = {} # 对象 a 的引用计数为 1
b = {} # 对象 b 的引用计数为 1
a['b'] = b # 对象 b 的引用计数 +1 = 2
b['a'] = a # 对象 a 的引用计数 +1 = 2
del a # a 的 ref -1,=1
def b # b 的 ref -1,=1
事实上,Python 本身能够处理这种情况, 可以显示调用 gc.collect(),来启动垃圾回收。
import os
import psutil
import gc
# 显示当前 python 程序占用的内存大小
def show_memory_info(hint):
pid = os.getpid()
p = psutil.Process(pid)
info = p.memory_full_info()
memory = info.uss / 1024 / 1024
print("{} memory used: {} MB".format(hint, memory))
def func_1():
show_memory_info('initial')
a = [i for i in range(10000000)]
b = [i for i in range(10000000)]
show_memory_info('after a,b created')
a.append(b)
b.append(a)
if __name__ == "__main__":
# func_1()
# show_memory_info('finished')
func_1()
gc.collect()
show_memory_info('finished')
#############输出################
initial memory used: 8.49609375 MB
after a,b created memory used: 783.20703125 MB
finished memory used: 783.20703125 MB
initial memory used: 8.3984375 MB
after a,b created memory used: 783.1796875 MB
finished memory used: 8.73828125 MB
所以,Python 的垃圾回收机制并没有那么弱。
【2】标记-清除
在 Python 中,垃圾回收机制主要使用了标记清除算法(Mark and Sweep)来管理内存。Python 的标准解释器 CPython 使用了这种垃圾回收机制。
CPython 的标记清除算法工作原理如下:
标记阶段(Mark):
- 从根对象开始,通常是全局变量、模块、当前执行堆栈等,标记所有可访问的对象。
- 这一过程通过遍历对象之间的引用关系来实现,将可达的对象标记为活动对象。
清除阶段(Sweep):
- 在清除阶段,Python 的垃圾回收器会遍历整个堆内存,回收未标记的对象所占用的内存空间。
- 未被标记的对象即被视为垃圾对象,它们所占用的内存将被释放。
- 此外,清除阶段还负责处理循环引用的情况,即相互引用但已不可达的对象。
Python 的垃圾回收机制是自动运行的,它通过引用计数和周期性的标记清除来管理内存。引用计数用于跟踪对象的引用数量,当引用计数为零时,对象即被认定为垃圾并被回收。而标记清除算法则处理循环引用等无法通过引用计数解决的情况。
需要注意的是,标记清除算法在进行垃圾回收时会导致一定的性能开销和停顿时间。为了减少这种影响,Python 也提供了其他优化技术和算法,比如分代回收(Generational Garbage Collection)和增量垃圾回收(Incremental Garbage Collection)等。
总结起来,Python 的标准解释器 CPython 使用标记清除算法来管理内存并进行垃圾回收,保证程序的内存使用效率和稳定性。
【3】分代回收
分代回收的基本思想是根据对象的存活时间将其划分为不同的代,通常将所有对象划分为三代:0代、1代和2代。新创建的对象会被放入 0 代,而经过一次垃圾回收仍然存活的对象会被提升到更高的代中。当某一代中的对象经过多次垃圾回收仍然存活下来,就可以认为它的存活概率较高,需要更少的垃圾回收操作。
Python 的分代回收主要基于以下两个原理:
弱代假设 (Weak Generational Hypothesis):
- 大部分对象在内存中存在的时间很短,即新创建的对象往往很快就变得不可达。
- 较早创建的对象,即存活时间较长的对象,更有可能在未来继续存活下来。
代之间的存活率关系 (Generation Survival Rate):
- 0 代中的对象一般存活时间最短,因此需要更频繁地进行垃圾回收。
- 1 代中的对象相对较少存活,需要相对较少的垃圾回收操作。
- 2 代中的对象存活率更高,需要较少的垃圾回收操作。
Python 的分代回收机制会根据这些原理,定期对不同代进行垃圾回收操作。通常,每次垃圾回收后,存活的对象会被提升到更高的代中。这样一来,垃圾回收机制可以更加集中地处理那些存活时间较长的对象,从而提高垃圾回收效率。
分代回收是 Python 垃圾回收机制的重要组成部分,它通过根据对象的存活时间进行优化,提高了垃圾回收的效率和性能。这对于 Python 这种动态语言来说尤为重要,因为它经常面临着频繁的内存分配和回收操作。
要是还不懂
个人见解:
1. 如何定义垃圾数据?
name = 'kevin'
name = 'tank'
2. 如何回收、清除
"""在Python中,已经开发了一套成熟的垃圾回收方案!"""
1. 引用计数
一个变量值如果有一个变量名指向,那么,在它身上就计数1,引用计数为0的数据都为垃圾数据,我们也会把引用计数为0的数据清除点
2. 标记清除
# 原来比较复杂,你只需要记住它的大致原来怎么回事就行了
当我们的内存空间即将要沾满的时候,这个时候会暂停所有程序的执行,开始扫描内存空间中得数据,把垃圾数据进行打标签,然后统一进行垃圾数据的清除.
3. 分代回收
对垃圾数据的监管频率逐渐下降