带你解析Python垃圾回收机制
文章目录
24 | 带你解析 Python 垃圾回收机制
你好,我是景霄。
众所周知,我们当代的计算机都是图灵机架构。图灵机架构的本质,就是一条无限长的纸带,对应着我们今天的存储器。在工程学的演化中,逐渐出现了寄存器、易失性存储器(内存)和永久性存储器(硬盘)等产品。其实,这本身来自一个矛盾:速度越快的存储器,单位价格也越昂贵。因此,妥善利用好每一寸高速存储器的空间,永远是系统设计的一个核心。
回到 Python 应用层。
我们知道,Python 程序在运行的时候,需要在内存中开辟出一块空间,用于存放运行时产生的临时变量;计算完成后,再将结果输出到永久性存储器中。如果数据量过大,内存空间管理不善就很容易出现 OOM(out of memory),俗称爆内存,程序可能被操作系统中止。
而对于服务器,这种设计为永不中断的系统来说,内存管理则显得更为重要,不然很容易引发内存泄漏。什么是内存泄漏呢?
这里的泄漏,并不是说你的内存出现了信息安全问题,被恶意程序利用了,而是指程序本身没有设计好,导致程序未能释放已不再使用的内存。
内存泄漏也不是指你的内存在物理上消失了,而是意味着代码在分配了某段内存后,因为设计错误,失去了对这段内存的控制,从而造成了内存的浪费。
那么,Python 又是怎么解决这些问题的?换句话说,对于不会再用到的内存空间,Python 是通过什么机制来回收这些空间的呢?
计数引用
我们反复提过好几次, Python 中一切皆对象。因此,你所看到的一切变量,本质上都是对象的一个指针。
那么,怎么知道一个对象,是否永远都不能被调用了呢?
我们上节课提到过的,也是非常直观的一个想法,就是当这个对象的引用计数(指针数)为 0 的时候,说明这个对象永不可达,自然它也就成为了垃圾,需要被回收。
我们来看一个例子:
|
|
def func(): show_memory_info(‘initial’) a = [i for i in range(10000000)] show_memory_info(‘after a created’)
func() show_memory_info(‘finished’)
########## 输出 ##########
initial memory used: 47.19140625 MB after a created memory used: 433.91015625 MB finished memory used: 48.109375 MB
|
|
def func(): show_memory_info(‘initial’) global a a = [i for i in range(10000000)] show_memory_info(‘after a created’)
func() show_memory_info(‘finished’)
########## 输出 ##########
initial memory used: 48.88671875 MB after a created memory used: 433.94921875 MB finished memory used: 433.94921875 MB
|
|
def func(): show_memory_info(‘initial’) a = [i for i in derange(10000000)] show_memory_info(‘after a created’) return a
a = func() show_memory_info(‘finished’)
########## 输出 ##########
initial memory used: 47.96484375 MB after a created memory used: 434.515625 MB finished memory used: 434.515625 MB
|
|
import sys
a = []
# 两次引用,一次来自 a,一次来自 getrefcount print(sys.getrefcount(a))
def func(a): # 四次引用,a,python 的函数调用栈,函数参数,和 getrefcount print(sys.getrefcount(a))
func(a)
# 两次引用,一次来自 a,一次来自 getrefcount,函数 func 调用已经不存在 print(sys.getrefcount(a))
########## 输出 ##########
2 4 2
|
|
import sys
a = []
print(sys.getrefcount(a)) # 两次
b = a
print(sys.getrefcount(a)) # 三次
c = b d = b e = c f = e g = d
print(sys.getrefcount(a)) # 八次
########## 输出 ##########
2 3 8
|
|
import gc
show_memory_info(‘initial’)
a = [i for i in range(10000000)]
show_memory_info(‘after a created’)
del a gc.collect()
show_memory_info(‘finish’) print(a)
########## 输出 ##########
initial memory used: 48.1015625 MB after a created memory used: 434.3828125 MB finish memory used: 48.33203125 MB
NameError Traceback (most recent call last)
NameError: name ‘a’ is not defined
|
|
def func(): 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)
func() show_memory_info(‘finished’)
########## 输出 ##########
initial memory used: 47.984375 MB after a, b created memory used: 822.73828125 MB finished memory used: 821.73046875 MB
|
|
import gc
def func(): 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)
func() gc.collect() show_memory_info(‘finished’)
########## 输出 ##########
initial memory used: 49.51171875 MB after a, b created memory used: 824.1328125 MB finished memory used: 49.98046875 MB
|
|
import objgraph
a = [1, 2, 3] b = [4, 5, 6]
a.append(b) b.append(a)
objgraph.show_refs([a])
|
|
import objgraph
a = [1, 2, 3] b = [4, 5, 6]
a.append(b) b.append(a)
objgraph.show_backrefs([a])
|
|
文章作者 anonymous
上次更新 2024-05-26