PythonTip >> 博文 >> 基础与入门

Python内存泄漏查看器实现

zihua 2013-10-10 16:10:24 点击: 2634 | 收藏


  为更好定位Python内存泄漏情况,写了一个新的Python内存泄漏查看器模块。
模块有如下功能:
  * 将所有对象按框图引用关系输出png图片
  * 可调整输出每引用层节点的最大数量
  * 可调整泄漏对象往上引用的层次数量
  * 各种类型对象用唯一的颜色标识
  * 泄漏对象在原颜色上染红色
  * 泄漏对象相关的link用红色线条/红色文字
  * 用<>特别标识Python对象的特殊结构link文字

查看插图

  所以,根据上图,需要去除内存泄漏要做的工作就是【消除红色框】,
最简单的方式是【消除所有红色link】,但这显然不是最有效的方法————
最有效的方法是尽量切断"顶部"的link(怎么才算"顶部",依赖功能逻辑判定)。

---------------------------------------------------------------------

  Python提供了gc.get_referrers方法来获得一个对象的引用者列表,很大程度
上,这已经完成了模块的50%的功能了:因为只要while循环取到get_referrers
并入出双端队列,直至处理为空即可。

  但是在输出对象引用关系的有向边信息时,需要仔细地对types模块中所
定义的各种类型的Python对象进行区别对待,甚至有些Python的内部对象类型
连types模块中都没给出,或连Python Manual手册上都不多说一句 :( :
  (1)CellType: 一种用于指向upvalue(Lua中这样叫,就这样称呼吧)的对象,
       它可被一个或多个clousure的free var tuple引用。虽然Python不希望
       使用者注意这个内部的对象类型,但是可以自己动手取得:
       CellType = type((lambda x: lambda: x)(1).func_closure[0])

 (2)NewStyleClassType: 就是new style class(和old style class相对的)。
    types.ClassType为old-style class;
    types.TypeType才是new-style class;
    PS:Python从2.2后引入new-style class以来就提倡摒弃old style class
    无论从性能上,方便性上,功能多样性上new style class均优;
    而且old-style class最终会从Python中除去,所以,忘记它吧。


  还有一个细节:当获得一个NewStyleClass对象clsobj时,不能想当然地判断:
      clsobj.__dict__ is 【被引用对象】
(is是Python中比对两个对象地址的操作符,a is b 等价于 id(a) == id(b))
因为 clsobj.__dict__ 是一个types.DictProxyType类型的对象;
(gc.get_referrers对这种proxy类型对象的tp_traverse是相应转接
实际dict对象的,具体见:descrobject.c中的proxytype实现。
Python用了Proxy机制来保护clsobj中的属性不被轻易更改)
实际上clsobj.__dict__并不是class中的dict对象本身,所以应该用DictProxy
对象的tp_compare函数来判断了,即用 == 判断:
      clsobj.__dict__ == 【被引用对象】

 

  最后,查找泄漏前,最好设置一个ignore_id set,可以将查找模块中不希望
暴露在最终关系图中的对象id都放进去(比如查找函数的locals(),等等...);
毕竟总要保证泄漏模块不能成为泄漏源,所以没必要输出模块中的对象关系。

---------------------------------------------------------------------

  真正使用时,通常系统中存在大量的泄漏对象,如果一次性把它们都画在一
张png上,关系错综复杂。所以一个较好的方法是将每N个泄漏对象调用一次接口
画在一张png关系图上,迭代画完成所有泄漏对象,然后逐张消灭;根据经验,
20个以下的泄漏对象打印在一张图上的复杂度是可以接受的。

  泄漏对象往上引用的层次数量参数不宜过大,3到8层的关系图足可以确定
泄漏途径并加以解决,过多的层次只会让人眼花缭乱。

  每引用层节点的最大数量参数方面,越大则越接近完全引用关系图;但是,
30以下的数量已经比较足够了,其实越少越好生成图也就越快:因为很多时候
查证人只需知道少数几个link的信息就能定位解决泄漏了。

---------------------------------------------------------------------

  PS: 在一个复杂的Python程序环境中,看对象引用关系图可以发现一些很有趣
的引用方式,有助于理解Python内部对象结构,编码时也会留个心眼防止泄漏:)

---------------------------------------------------------------------

  我们应该在项目编写之初便建立良好的内存泄漏检查机制,甚至自动发现并提醒。
Python提供了weakref, gc, inspect等模块,适当使用就能起到很好的效果。
  
---------------------------------------------------------------------

 

相关链接:

   http://www.cafepy.com/article/python_types_and_objects/python_types_and_objects.html
   http://docs.python.org/release/2.2.3/whatsnew/sect-rellinks.html
   http://mg.pov.lt/objgraph/
   http://www.graphviz.org/


PS:
   感谢xiaobing jiang同学指出types.TypeType类型~ :)

---------------------------------------------------------------------

原文链接:http://pythoner.org/wiki/56/

作者:zihua | 分类: 基础与入门 | 标签: 基础与入门 | 阅读: 2634 | 发布于: 2013-10-10 16时 |