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

python内存泄露查找的更多相关文章

  1. python 内存泄露的诊断 - 独立思考 - ITeye技术网站

    python 内存泄露的诊断 - 独立思考 - ITeye技术网站 python 内存泄露的诊断 博客分类: 编程语言: Python Python多线程Blog.net  对于一个用 python ...

  2. 记一次调试python内存泄露的问题

    转载:http://www.jianshu.com/p/2d06a1a01cc3 这两天由于公司需要, 自己编写了一个用于接收dicom文件(医学图像文件)的server. 经过各种coding-de ...

  3. 排查python内存泄露中几个工具的使用

    本文主要介绍3个工具:pdb,objgraph,以及pympler. 1.pdb pdb是专门用于python代码调试,模仿gdb. 使用pdb可以查看堆栈,打印变量等. 这里介绍的是命令行下的pdb ...

  4. 关于排查python内存泄露的简单总结

    这次的内存泄露问题是发生在多线程场景下的. 各种工具都试过了,gc,objgraph, pdb,pympler等,仍然没有找到问题所在. pdb感觉用起来很方便,可以调试代码,对原来的代码无侵入性. ...

  5. python内存泄露memory leak排查记录

    问题描述 A服务,是一个检测MGR集群主节点是否发生变化的服务,使用python语言实现的. 针对每个集群,主线程会创建一个子线程,并由子线程去检测.子线程会频繁的创建和销毁. 上线以后,由于经常会有 ...

  6. 使用gc、objgraph干掉python内存泄露与循环引用!

    Python使用引用计数和垃圾回收来做内存管理,前面也写过一遍文章<Python内存优化>,介绍了在python中,如何profile内存使用情况,并做出相应的优化.本文介绍两个更致命的问 ...

  7. python内存泄露的诊断(转)

    本篇文章非原创,转载自:http://rstevens.iteye.com/blog/828565 . 对于一个用 python 实现的,长期运行的后台服务进程来说,如果内存持续增长,那么很可能是有了 ...

  8. python 内存泄露的诊断

    对于一个用 python 实现的,长期运行的后台服务进程来说,如果内存持续增长,那么很可能是有了"内存泄露" 一.内存泄露的原因 对于 python 这种支持垃圾回收的语言来说,怎 ...

  9. <转载>linux下内存泄露查找、BUG调试

    先收藏着,抽空好好看看:http://www.ibm.com/developerworks/cn/linux/l-pow-debug/ 简介 调试程序有很多方法,例如向屏幕上打印消息,使用调试器,或者 ...

随机推荐

  1. 【flink training】 打车热点区域实时统计PopularPlaces

    http://training.data-artisans.com/是Apache Flink商业公司DataArtisans提供的一个flink学习平台,主要提供了一些业务场景和flink api结 ...

  2. 关于linux PPA源问题

    添加PPA: 1.首先进入ubuntu系统,system—>administration—>update manager—>setting,在软件源界面,点击other softwa ...

  3. pow函数

    pow函数如果直接强制类型转换成int,会导致精度的损失.如果是int的幂计算,建议重写函数.或者用double型进行计算.

  4. 凑成整数x----二进制枚举

    给n个数,给定x. 用这n个数求和凑成x,问有多少种凑法. #include<bits/stdc++.h> using namespace std; int main() { int n, ...

  5. JQuery EasyUI 请求/加载 两次

    解决方案如下: Html页面中的Table标签中包含class属性(class="easyui-datagrid"),删除即可.

  6. vim 设置

    TL;DR: $ git clone https://github.com/sontek/dotfiles.git $ cd dotfiles $ ./install.sh vim Download  ...

  7. java+lucene中文分词,来看看百度究竟是怎么找到你想要的(十分重要,楼主幸苦之作)

    我想只要是学过数据库的孩纸,不管是mysql,还是sqlsever,一提到查找,本能的想到的便是like关键字,其实去转盘网(分类模式)之前也是采用这种算法,但我可以告诉大家一个很不幸的事情,like ...

  8. 第一百四十一节,JavaScript,封装库--DOM加载

    JavaScript,封装库--DOM加载 DOM加载,跨浏览器封装DOM加载,当网页文档结构加载完毕后执行函数,不等待图片音频视频等文件加载完毕 /** dom_jia_zai()函数,DOM页面加 ...

  9. 获取一个Assembly中的命名空间列表

    通过System.Reflection.Assembly类中提供的方法和属性不能直接获取组件中的命名空间列表.但有方法可以直接获得Assembly中的所有类型,我们便可以通过获取的类型来得到命名空间名 ...

  10. mfc小工具开发之定时闹钟之---二十四小时时区和时间段

    1.凌晨0:00-6:00时显示凌晨,上午6:00-12:00显示上午,中午12:00-14:00显示中午,下午14:00-显示下午,晚上18:00-24:00显示晚上 2. 早上:6-8:上午8-1 ...