zoukankan      html  css  js  c++  java
  • python内存泄露查找

    1 前言:

    1.1 像Java程序一样,虽然Python本身也有垃圾回收的功能,但是同样也会产生内存泄漏的问题

    1.2 在Python程序里,内存泄漏是由于一个长期持有的对象不断的往一个dict或者list对象里添加新的对象, 而又没有即时释放,就会导致这些对象占用的内存越来越多,从而造成内存泄漏。另外,对象的交叉引用也会造成内存无法释放的问题。

    2 那么如果在Python里发现了内存泄漏的问题,如何去查找呢?本文讲述了如何使用objgraph这个工具来进行内存泄漏的查找

    3 先下载objgraph这个工具:

    3.1 objgraph

    http://mg.pov.lt/objgraph/

    3.2 https://pypi.python.org/pypi/objgraph

    3.3 pythonsetup.py install 进行安装

    4 安装graphviz

    4.1 这是一个将图结构转化成png图片表示的工具,有了它,就可以通过对象的引用关系,为最终找到内存泄漏的对方提供最好的指导

    4.2 windows版下载地址:

    http://www.graphviz.org/Download_windows.php

    4.3 ubuntu下安装:

    sudo apt-get install graphviz

    5 这个工具还可以利用graphviz这个工具来生成可视化的对象引用关系图,但是根据个人的使用经验,在对象比较多的时候,生成的图往往会比较大

    6 如何查找产生泄漏的对象:

    6.1 objgraph.show_growth()

    这个函数可以说是这个工具中最有用的函数了

    作用是输出增长的对象。

    6.2 先从一个例子看看怎样用:

    import os

    import gc

    import objgraph

    gc.collect()

    print'====================================='

    objgraph.show_growth()

    a = []

    print'====================================='

    objgraph.show_growth()

    a.append([1,2,3])

    print'====================================='

    objgraph.show_growth()

    b = ['a','b','c']

    del b

    print'===================================='

    objgraph.show_growth()

    6.3 输出如下:

    =====================================

    wrapper_descriptor            1020    +1020

    function                      975      +975

    builtin_function_or_method     615      +615

    dict                          414      +414

    method_descriptor             391      +391

    weakref                       286      +286

    member_descriptor             192      +192

    tuple                         181      +181

    list                          159      +159

    getset_descriptor             132      +132

    =====================================

    wrapper_descriptor     1031      +11

    member_descriptor      196        +4

    getset_descriptor      135        +3

    weakref               289        +3

    dict                  417        +3

    list                  160        +1

    =====================================

    list      161       +1

    ====================================

    6.4 从打印可以看出:

    第一次调用show_growth时,实际上打印出来的是当前所有对象的总数

    第二次调用show_growth时,可以看到list对象增长了1,这正是a = []所创建的,其它增长的对象应该是在第一次调用show_growth函数内部产生的。

    当调用a.append([1,2,3])后,再调用show_growth,又发现list对象增长了1个

    再接下来,调用b =['a','b','c'] 后又马上调用del b把这个对象删除,再调用show_growth时对象没有增长。

    6.5 从上面的例子来看,show_growth可以准确的打印出增长的对象以及增长的个数。

    6.6 在实际情况中,通常为了查找出哪些对象有内存泄漏,一般用每隔一段时间调用一次show_growth的方法,

    6.7 然后找出对象的个数一直在增长的对象,这些对象即为发生了内存泄漏的对象。

    6.8 说明:

    为了使show_growth的输出更为准确,在调用show_growth时,最好调用gc.collect()进行一次垃圾对象的回收。

    7 如何定位到产生内存泄漏的代码?

    7.1 上述方法只能定位到哪些对象产生的内存泄漏,只能告诉我们有内存泄漏的产生,但是找出产生内存泄漏的代码才是我们的目的。

    7.2 但是用这个函数还是不能定位到具体的是那些代码里产生泄漏的,特别是dict和list这两个对象被许多模块,包括python解释器本身大量的使用,

    7.3 如果不能有更好办法,要定位到具体的位置真有点像是大海捞针,为了将自己创建的dict和list与其它模块的dict和list驱分开,我们定义一个新的dict和list类,方式如下:

    class Dict(dict):

        def__init__(self,args={}):

           dict.__init__(self,args)

    class List(list):

        def__init__(self,args=()):

           list.__init__(self,args

    )

    7.4 使用方法也很简单,如:

    d = Dict({'abc",123})

    l = List((1,2,3))

    7.5 我们修改一下上面的例子:

    class Dict(dict):

        def__init__(self,args={}):

           dict.__init__(self,args)

    class List(list):

        def__init__(self,args=()):

           list.__init__(self,args)

    import os

    import gc

    import objgraph

    gc.collect()

    print '====================================='

    objgraph.show_growth()

    a = List()

    print'====================================='

    objgraph.show_growth()

    a.append(List((1,2,3)))

    print'====================================='

    objgraph.show_growth()

    b = List(('a','b','c'))

    del b

    print '===================================='

    objgraph.show_growth()

    7.6 输出为:

    =====================================

    wrapper_descriptor            1020    +1020

    function                      977      +977

    builtin_function_or_method     615      +615

    dict                          416      +416

    method_descriptor             391      +391

    weakref                       288      +288

    member_descriptor             192      +192

    tuple                         184      +184

    list                          160      +160

    getset_descriptor             136      +136

    =====================================

    wrapper_descriptor     1031      +11

    member_descriptor      196        +4

    getset_descriptor      139        +3

    weakref               291        +3

    dict                  419        +3

    List                    1        +1

    =====================================

    List        2       +1

    ====================================

    7.7 这样,就可以将在自己的代码里使用的list和其它代码里使用的list区分开了。

    7.8 范围又可以进一步的缩小了,离目标又近了一步,但是还是有一段距离。

    7.9 这时几个函数该出场了:

    objgraph.by_type

    这个函数通过类名可以查到所有该类的对象,例如objgraph.by_type('list')将返回所有的list对象。

    objgraph.find_backref_chain

    这个函数可以用来查找对象的引用树

    objgraph.show_chain

    生成png图片格式的对象引用关系

    该函数要使用到

    7.10 再看下最终的代码:

    import os

    import gc

    import objgraph

    import inspect

    class Dict(dict):

        def__init__(self,args={}):

           dict.__init__(self,args)

    class List(list):

        def__init__(self,args=()):

           list.__init__(self,args)

    class MyClass:

        def __init__(self):

           self.a = []

           d1 = Dict({1:1})

           d2 = Dict({2:2})

           l = List((1,2,3))

           self.a.append(d1)

           self.a.append(d2)

    c = MyClass()

    print 'objgraph.by_type:',objgraph.by_type('Dict')

    chain =objgraph.find_backref_chain(objgraph.by_type('Dict')[-1],inspect.ismodule)

    objgraph.show_chain(chain,filename='chain.png')

    7.11 最终的输出如图:




    7.12 从该图中可以基本定位到Dict对象是在MyClass中分配的。

    8 由于会产生内存泄漏的重点是dict和list这两个类,所以这里就研究下怎么查找dict和list产生的内存泄漏。

    9 总结一下:

    9.1 实际上,由于python写的代码往往非常清晰,只要平时在写代码时留个心眼,python的内存泄漏的问题是可以很好的避免的。

    9.2 在自己做的项目中,曾经一直担心会产生内存泄漏的问题,但是实际上却至今没有发现过。

    9.3 当然,有可能是自己做的python项目不够多,不够大,最大的项目允其量也就是五六万行代码而己。

    9.4 但是,防犯于未然,忧患意识,是从事任何行业所必须具备的休养,软件行业也不例外。

    9.5 如果等真的出了问题,解决起来会比较麻烦倒是其次的,其造成的损失和影响才是主要的。

    参考博客:http://blog.csdn.net/i2cbus/article/details/20155273

  • 相关阅读:
    Navigator对象关于语言的属性
    对ng-repeat的表格内容添加不同样式:ng-style
    ng-repeat
    ES 6 : 函数的扩展
    二维码下载,区分是 ios 和 android
    gulp配置备份
    [转]移动端web页面使用字体的思考
    移动开发总结
    前端优化:雅虎35条
    input填写银行卡号,每四位空一隔
  • 原文地址:https://www.cnblogs.com/1204guo/p/7978427.html
Copyright © 2011-2022 走看看