05 python内存泄漏

egon新书来袭请看https://egonlin.com/book.html

python内存泄露

起因

内存泄露指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。导致程序运行速度减慢甚至系统崩溃等严重后果。有 del() 函数的对象间的循环引用是导致内存泄漏的主凶

方案

不使用一个对象时使用:delobject 来删除一个对象的引用计数就可以有效防止内存泄漏问题。通过Python 扩展模块 gc 来查看不能回收的对象的详细信息。可以通过 sys.getrefcount(obj) 来获取对象的引用计数,并根据返回值是否为 0 来判断是否内存泄漏。

但是由于gc垃圾收集机制,要遍历所有被垃圾收集器管理的python对象(包括垃圾和非垃圾对象),该过程比较耗时可能会造成程序卡顿,会对某些对内存、cpu要求较高的场景造成性能影响。那怎么才能优雅地避免内存泄露呢?

编写安全的代码

比如对于下面发生内存泄露的cycle_ref函数,在函数结束前解除循环引用,即可解决内存泄露问题。

def cycle_ref():
    a1 = A()
    a2 = A()
 
    a1.child = a2
    a2.child = a1
 
    # 解除循环引用,避免内存泄露
    a1.child  = None
    a2.child  = None

但是对于上述方法,我们有可能会忘记那一两行无关紧要的代码而造成灾难性后果,毕竟老虎也有打盹的时候。那怎么办?不要着急,Python已经为我们考虑到这点:弱引用。

弱引用

Python标准库提供了weakref模块,弱引用不会在引用计数中计数,其主要目的是解决循环引用。并非所有的对象都支持weakref,例如list和dict就不支持。下面是weakref比较常用的方法:

"""
1. class weakref.ref(object[, callback]) :创建一个弱引用对象,object是被引用的对象,callback是回调函数(当被引用对象被删除时,调用该回调函数)

2.weakref.proxy(object[, callback]):创建一个用弱引用实现的代理对象,参数同上

3.weakref.getweakrefcount(object) :获取对象object关联的弱引用对象数

4.weakref.getweakrefs(object):获取object关联的弱引用对象列表

5.class weakref.WeakKeyDictionary([dict]):创建key为弱引用对象的字典

6.class weakref.WeakValueDictionary([dict]):创建value为弱引用对象的字典

7.class weakref.WeakSet([elements]):创建成员为弱引用对象的集合对象
"""

同样对于上面发生内存泄露的cycle_ref函数,使用weakref稍加改造,便可更安全地解决内存泄露问题:

import weakref
 
class A(object):
    def __init__(self):
        self.data = [x for x in range(100000)]
        self.child = None
 
    def __del__(self):
        pass
 
def cycle_ref():
    a1 = A()
    a2 = A()
 
    a1.child = weakref.proxy(a2)
    a2.child = weakref.proxy(a1)
 
if __name__ == '__main__':
    import time
    while True:
        time.sleep(0.5)
        cycle_ref()
posted @ 2020-01-18 10:44  小猿取经-林海峰老师  阅读(1128)  评论(0编辑  收藏  举报