分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 网页技术

urlopen内存泄漏浅析

发布时间:2023-09-06 02:29责任编辑:林大明关键词:url

1.背景

  urllib,urllib2是客户端http协议的实现,urllib2底层使用httplib,socket库,它主要包含urlopen, build_opener, install_opener等func。python2.7使用urllib2库中的urlopen会出现内存泄漏的现象,可以通过gc模块来视察内存泄漏情况。

# -*- coding: utf-8 -*-#!usr/bin/pythonimport urllib2import socketimport gc# check memory on memory leaksdef get_unreachable_memory_len(): ???#当设置DEBUG_SAVEALL后,所有unreachable对象会append到garbage中,不会被销毁,从而进行视察,测试时使用。 ???gc.set_debug(gc.DEBUG_SAVEALL) ???gc.collect() ???unreachableL = [] ???for it in gc.garbage: ???????unreachableL.append(it) ???????#print(str(it)) ???print str(unreachableL)def task(): ???try: ???????req = urllib2.urlopen(‘http://www.baidu.com/‘, timeout=3) ???????text = req.read() ???????#req.fp._sock.recv = None ???????req.close() ???except urllib2.HTTPError, e: ???????print e.code ???except urllib2.URLError, e: ???????print e.reason ???else: ???????print("urlopen success")if __name__ == ‘__main__‘: ???get_unreachable_memory_len() ???print("-------------------------") ???task() ???print("-------------------------") ???get_unreachable_memory_len()

 运行程序确定urlopen存在内存泄漏:

 2.现象分析

   python垃圾回收机制基于对象的引用计数,所以先找到造成循环引用的代码。采用objgraph模块打印出增加的对象。示例代码如下:

# -*- coding: utf-8 -*-#!usr/bin/pythonimport urllib2import socketimport gcimport objgraph# check memory on memory leaksdef get_unreachable_memory_len(): ???#当设置DEBUG_SAVEALL后,所有unreachable对象会append到garbage中,不会被销毁,从而进行视察,测试时使用。 ???gc.set_debug(gc.DEBUG_SAVEALL) ???gc.collect() ???unreachableL = [] ???for it in gc.garbage: ???????unreachableL.append(it) ???????#print(str(it)) ???print str(unreachableL)def task(): ???try: ???????req = urllib2.urlopen(‘http://www.baidu.com/‘, timeout=3) ???????text = req.read() ???????#req.fp._sock.recv = None ???????req.close() ???except urllib2.HTTPError, e: ???????print e.code ???except urllib2.URLError, e: ???????print e.reason ???else: ???????print("urlopen success")#class HTTPResponse(object):# ???passif __name__ == ‘__main__‘: ???gc.set_debug(gc.DEBUG_SAVEALL) ???objgraph.show_growth() ???print("-------------------------") ???for i in range(5): ???????task() ???print("-------------------------") ???objgraph.show_growth()

 看到引用计数加5的三个字段,以及观察到上一次运行结果首先出现的是httplib.HTTPResponse。

使用objgraph.show_backrefs对httplib.HTTPResponse进行分析:

# -*- coding: utf-8 -*-#!usr/bin/pythonimport urllib2import socketimport gcimport objgraph# check memory on memory leaksdef get_unreachable_memory_len(): ???#当设置DEBUG_SAVEALL后,所有unreachable对象会append到garbage中,不会被销毁,从而进行视察,测试时使用。 ???gc.set_debug(gc.DEBUG_SAVEALL) ???gc.collect() ???unreachableL = [] ???for it in gc.garbage: ???????unreachableL.append(it) ???????#print(str(it)) ???print str(unreachableL)def task(): ???try: ???????req = urllib2.urlopen(‘http://www.baidu.com/‘, timeout=3) ???????text = req.read() ???????#req.fp._sock.recv = None ???????req.close() ???except urllib2.HTTPError, e: ???????print e.code ???except urllib2.URLError, e: ???????print e.reason ???else: ???????print("urlopen success")#class HTTPResponse(object):# ???passif __name__ == ‘__main__‘: ???gc.set_debug(gc.DEBUG_SAVEALL) ???print("-------------------------") ???for i in range(5): ???????task() ???print("-------------------------") ???objgraph.show_backrefs(objgraph.by_type(‘HTTPResponse‘)[0], max_depth = 10, filename = ‘obj.dot‘)

 将生成的obj.dot转化为obj.png(使用命令dot obj.dot -Tpng -o obj.png)图示如下,记录下造成循环引用的recv引用和read方法。

3.源码追踪

 查看urllib2类图可以使用pycharm自动生成UML类图,这里需要分析urllib2.urlopen的调用流程,可以引入pycallgraph模块来分析,示例代码入下:

# -*- coding: utf-8 -*-#!usr/bin/pythonimport urllib2import socketimport gcfrom pycallgraph import PyCallGraphfrom pycallgraph.output import GraphvizOutputdef task(): ???graphviz = GraphvizOutput() ???graphviz.output_file = ‘urlopen.png‘ ???with PyCallGraph(output=graphviz): ???????try: ???????????req = urllib2.urlopen(‘http://www.baidu.com/‘, timeout=3) ???????????#text = req.read() ???????????#req.fp._sock.recv = None ???????????#req.close() ???????except urllib2.HTTPError, e: ???????????print e.code ???????except urllib2.URLError, e: ???????????print e.reason ???????else: ???????????print("urlopen success")if __name__ == ‘__main__‘: ???task()

 截取部分生成的调用流程图:

在HTTPHandler类中的do_open方法中有这一行代码:

这个r指的是HTTPResopnse类,它只有read方法而没有recv方法,这个引用在urlopen调用结束后并没有释放。解决内存泄漏问题就需要消除改引用。

4.解决方法:

1)上述示例当中调用task()之后使用gc.collect()进行手动内存回收。

2)http连接close之前手动解决r.recv这个引用。

 req = urllib2.urlopen(‘http://www.baidu.com/‘, timeout=3)text = req.read()#对于调用urlopen正常返回的情况手动解除r.recv = r.read这个引用req.fp._sock.recv = Nonereq.close()

 注:当返回错误状态码urllib2.HTTPError时无法生效,需要修改urllib2.py源码为

3)改用更底层的socket,httplib库。

参考资料:

1)http://python.jobbole.com/88827/

2)https://bugs.python.org/issue1208304

3)https://stackoverflow.com/questions/4214224/how-to-solve-python-memory-leak-when-using-urrlib2#

urlopen内存泄漏浅析

原文地址:https://www.cnblogs.com/anjike/p/10230302.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved