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
python内存泄露查找的更多相关文章
- python 内存泄露的诊断 - 独立思考 - ITeye技术网站
python 内存泄露的诊断 - 独立思考 - ITeye技术网站 python 内存泄露的诊断 博客分类: 编程语言: Python Python多线程Blog.net 对于一个用 python ...
- 记一次调试python内存泄露的问题
转载:http://www.jianshu.com/p/2d06a1a01cc3 这两天由于公司需要, 自己编写了一个用于接收dicom文件(医学图像文件)的server. 经过各种coding-de ...
- 排查python内存泄露中几个工具的使用
本文主要介绍3个工具:pdb,objgraph,以及pympler. 1.pdb pdb是专门用于python代码调试,模仿gdb. 使用pdb可以查看堆栈,打印变量等. 这里介绍的是命令行下的pdb ...
- 关于排查python内存泄露的简单总结
这次的内存泄露问题是发生在多线程场景下的. 各种工具都试过了,gc,objgraph, pdb,pympler等,仍然没有找到问题所在. pdb感觉用起来很方便,可以调试代码,对原来的代码无侵入性. ...
- python内存泄露memory leak排查记录
问题描述 A服务,是一个检测MGR集群主节点是否发生变化的服务,使用python语言实现的. 针对每个集群,主线程会创建一个子线程,并由子线程去检测.子线程会频繁的创建和销毁. 上线以后,由于经常会有 ...
- 使用gc、objgraph干掉python内存泄露与循环引用!
Python使用引用计数和垃圾回收来做内存管理,前面也写过一遍文章<Python内存优化>,介绍了在python中,如何profile内存使用情况,并做出相应的优化.本文介绍两个更致命的问 ...
- python内存泄露的诊断(转)
本篇文章非原创,转载自:http://rstevens.iteye.com/blog/828565 . 对于一个用 python 实现的,长期运行的后台服务进程来说,如果内存持续增长,那么很可能是有了 ...
- python 内存泄露的诊断
对于一个用 python 实现的,长期运行的后台服务进程来说,如果内存持续增长,那么很可能是有了"内存泄露" 一.内存泄露的原因 对于 python 这种支持垃圾回收的语言来说,怎 ...
- <转载>linux下内存泄露查找、BUG调试
先收藏着,抽空好好看看:http://www.ibm.com/developerworks/cn/linux/l-pow-debug/ 简介 调试程序有很多方法,例如向屏幕上打印消息,使用调试器,或者 ...
随机推荐
- 李洪强IOS经典面试题 33-计算有多少个岛屿
李洪强IOS经典面试题 33-计算有多少个岛屿 问题 在一个地图中,找出一共有多少个岛屿. 我们用一个二维数组表示这个地图,地图中的 1 表示陆地,0 表示水域.一个岛屿是指由上下左右相连的陆地,并且 ...
- eclipse调试的时候查看变量出现com.sun.jdi.InvocationException occurred invoking method.
症状:如题 分析/解决方案:你的toString抛出了异常,去查看toString的代码是不是有问题,比如说空指针什么的
- binutils工具集之---objcopy,ranlib,size,strings,strip
objcopy的作用是拷贝一个目标文件的内容到另一个目标文件中.objcopy使用GNU BFD库去读或写目标文件.objcopy可以使用不同于源目标文件的格式来写目的目标文件(也即是说可以将一种格式 ...
- 堆积木----vector防止内存超限
蒜头君有 nn 块积木,编号分别为 11 到 nn.一开始,蒜头把第 ii 块积木放在位置 ii.蒜头君进行 mm 次操作,每次操作,蒜头把位置 bb 上的积木整体移动到位置 aa 上面.比如 11 ...
- 如何查看iis的连接数量
引用:http://jingyan.baidu.com/article/54b6b9c0f3c2002d583b470d.html 运行,输入,perfmon.msc. 在系统监视器,区域点击,添 ...
- JVM Specification 9th Edition (1) Cover
这个就是Java虚拟机规范第9版的网页版封面了,上面是4个大牛的名字,先来了解以下吧,万一那天有幸遇见呢. Tim Lindholm Frank Yellin Gilad Bracha Alex Bu ...
- 让超出DIV宽度范围的文字自动显示省略号...
关键是:text-overflow: ellipsis; div.titleholder { font-family: ms sans serif, arial; font-size: 8pt; wi ...
- 页面中添加qq客服
html页面 <html> <head> <meta http-equiv="Content-Type" content="text/htm ...
- php 路由实现
因为有小的业务逻辑比较复杂,orm用起来很麻烦,但是多入口又不好控制,每个页面都去包含,多了就迷了,所以就只写了个路由,加了个防注入 封装增删改查好无聊,直接封装业务逻辑,为业务而开发,业务逻辑里是面 ...
- SQLServerException:通过端口 1433 连接到主机 localhost 的 TCP/IP 连接失败。
一.问题描述: 1.连接数据库时抛出的异常: com.microsoft.sqlserver.jdbc.SQLServerException: 通过端口 1433 连接到主机 localhost 的 ...