浅析Python垃圾回收机制!
Python垃圾回收机制
1. 内存泄露
- 程序本身没有设计好,导致程序未能释放已不再使用的内存
- 代码在分配了某段内存后,因为设计错误,失去了对这段内存的控制,从而造成了内存的浪费
监控python程序内存占用情况,psutil库
import os
import psutil
# 显示当前程序占用内存大小
def show_memory_info(hint):
pid = os.getpid() # 获取当前进程号
p = psutil.Process(pid)
info = p.memory_info()
memory = info.rss / 1024. / 1024
print(f"{hint} 内存用了:{memory}MB")
查看对象占用内存大小
import sys
a = [i for i in range(10000)]
memory = sys.getsizeof(a) / 1024
print(f"内存用了:{memory}KB")
# 内存用了:85.578125KB
2. Python什么时候启动垃圾回收机制?
2.1 计数引用
python中一切皆对象,所看到的一切变量,本质上都是对象的一个指针,当这个对象的引用次数为0的时候,说明这个对象永不可达,成为需要被回收的垃圾
# 查看引用次数
import sys
a = []
print(sys.getrefcount(a)) # 两次引用,一次a,一次getr
def func(a):
# 四次引用,函数调用会产生两次额外的引用,一次来自函数栈,另一个是函数参数
print(sys.getrefcount(a))
func(a)
2
4
def func():
show_memory_info('初始')
a = [i for i in range(1000000)] # 列表生成式
show_memory_info('创建之后')
func()
show_memory_info('结束之后') # 内存即可被释放
初始 内存用了:52.4609375MB
创建之后 内存用了:91.921875MB
结束之后 内存用了:53.80859375MB
2.2 循环引用
- python中使用标记清除算法和分代收集,来启动针对循环引用的自动垃圾回收
- 标记清除算法,图论中的不可达概念
- 分代收集算法中每一代都有一个默认阈值,超过指定阈值之后就会启动垃圾回收,如果垃圾回收启动太频繁,会造成程序性能低下,分代收集为了提高性能,因此不立刻回收。
def func():
show_memory_info('初始')
a = [i for i in range(1000000)] # 列表生成式
b = [i for i in range(1000000)] # 列表生成式
show_memory_info('创建之后')
a.append(b)
b.append(a)
func()
show_memory_info('结束之后') # 可以看到循环引用之后,内存依旧被占用
初始 内存用了:77.125MB
创建之后 内存用了:163.8828125MB
结束之后 内存用了:163.8828125MB
显示调用gc.collect()来启动垃圾回收
import gc
def func():
show_memory_info('初始')
a = [i for i in range(1000000)] # 列表生成式
b = [i for i in range(1000000)] # 列表生成式
show_memory_info('创建之后')
a.append(b)
b.append(a)
func()
# 显示调用gc.collect()来启动垃圾回收
gc.collect()
show_memory_info('结束之后')
初始 内存用了:77.609375MB
创建之后 内存用了:145.92578125MB
结束之后 内存用了:77.609375MB
问题:引用计数是0是启动垃圾回收的充要条件吗?
引用计数是其中最简单的实现,不是充要条件,只能算作充分非必要条件,循环引用需要通过不可达判定,来确定是否可以回收。python中自动回收算法包括标记清除算法和分代收集。