python拾遗7 - 垃圾回收
2020-05-05 本文已影响0人
天命_风流
垃圾回收机制
相比 C++ 的手动回收内存,python 的垃圾回收机制可谓是省心省力,判断是否回收一块内存,主要有三点:
1 计数引用
也就是最为常见的回收方式,当对一块内存的引用计数为 0 时,证明没有方法再调用这块内存,执行回收。
另你可以使用 sys.getrefcount( a ) 来查看一个变量的引用情况
2 标记清除
上面的方式非常简单,但是 python 的回收机制没有这么简单,当产生循环引用的时候,第一种方法就无能为力了:
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
这时,我们要使用标记清除算法,我们可以用图的概念理解它:对一个有向图,如果从一个指定的结点出发,遍历所有结点,并进行标记。遍历结束后,没有被标记的节点就是不可达节点。
显然,这种不可达的节点是没有意义的,我们可以回收它的内存。
3 分代收集
引用专栏中的话解释:
而分代收集算法,则是另一个优化手段。
Python 将所有对象分为三代。刚刚创立的对象是第 0 代;经过一次垃圾回收后,依然存在的对象,便会依次从上一代挪到下一代。而每一代启动自动垃圾回收的阈值,则是可以单独指定的。当垃圾回收器中新增对象减去删除对象达到相应的阈值时,就会对这一代对象启动垃圾回收。
事实上,分代收集基于的思想是,新生的对象更有可能被垃圾回收,而存活更久的对象也有更高的概率继续存活。因此,通过这种做法,可以节约不少计算量,从而提高 Python 的性能。
4 手动删除
- 使用 del a 删除一条引用
- 使用 gc.collect( ),触发一次垃圾回收 ,回收内容包括上面三点。
调试内存引用
使用 objgraph 模块,可以生成一个可视化的引用关系图。
- objgraph.show_refs() 函数
import objgraph
a = [1, 2, 3]
b = [4, 5, 6]
a.append(b)
b.append(a)
objgraph.show_refs([a])
引用关系
- objgraph.show_backrefs( ) 函数
import objgraph
a = [1, 2, 3]
b = [4, 5, 6]
a.append(b)
b.append(a)
objgraph.show_backrefs([a])
这个函数展示的信息更加全面