当前位置: 代码迷 >> python >> 使用urrlib2时如何解决Python内存泄漏?
  详细解决方案

使用urrlib2时如何解决Python内存泄漏?

热度:116   发布时间:2023-06-13 14:08:47.0

我正在尝试为手机编写一个简单的Python脚本,以使用urrlib2定期加载网页。 实际上,我并不真正在乎服务器响应,我只想将URL中的某些值传递给PHP。 问题在于,用于S60的Python使用旧的2.5.4 Python内核,该内核似乎在urrlib2模块中存在内存泄漏。 在我读到的文章中,每种类型的网络通信中也都存在此类问题。 几年前已经报告此错误,同时也发布了一些解决方法。 在Google的帮助下,我已经尽力尝试了在该页面上可以找到的所有内容,但是在加载约70页后,我的手机仍会用完内存。 奇怪的是,Garbege Collector似乎也没有任何区别,只是使我的脚本慢得多。 据说,更新的(3.1)内核可以解决此问题,但是很遗憾,我迫不及待地等待了一年或更长时间才能使用S60端口。

添加了我发现的所有小技巧后,我的脚本的外观如下:


import urrlib2, httplib, gc
while(true):
 url = "http://something.com/foo.php?parameter=" + value 
 f = urllib2.urlopen(url)
 f.read(1)
 f.fp._sock.recv=None # hacky avoidance
 f.close()
 del f
 gc.collect()
有什么建议,如何使其永久工作而不会出现“无法分配内存”错误? 感谢您的提前,加油,b_m

更新:在内存用尽之前,我已经设法连接了92次,但是仍然不够好。

update2:尝试过前面建议的套接字方法,这是迄今为止第二好的(错误的)解决方案:


import socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.connect(("something.com", 80))
socket.send("test") #returns 4 (sent bytes, which is cool)
socket.send("test") #4
socket.send("test") #4
socket.send("GET /foo.php?parameter=bar HTTP/1.0\r\n\r\n") #returns the number of sent bytes, ok
socket.send("GET /foo.php?parameter=bar HTTP/1.0\r\n\r\n") #returns 0 on the phone, error on Windows7*
socket.send("GET /foo.php?parameter=bar HTTP/1.0\r\n\r\n") #returns 0 on the phone, error on Windows7*
socket.send("test") #returns 0, strange...
我也从上面尝试了一些小技巧。 上传约50次后线程关闭(电话还剩下50MB的内存,显然Python shell没有。)

更新 :我想我越来越接近解决方案! 我尝试发送多个数据而没有关闭并重新打开套接字。 这可能是关键,因为此方法只会留下一个打开的文件描述符。 问题是:

 import socket s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket.connect(("something.com", 80)) socket.send("test") #returns 4 (sent bytes, which is cool) socket.send("test") #4 socket.send("test") #4 socket.send("GET /foo.php?parameter=bar HTTP/1.0\\r\\n\\r\\n") #returns the number of sent bytes, ok socket.send("GET /foo.php?parameter=bar HTTP/1.0\\r\\n\\r\\n") #returns 0 on the phone, error on Windows7* socket.send("GET /foo.php?parameter=bar HTTP/1.0\\r\\n\\r\\n") #returns 0 on the phone, error on Windows7* socket.send("test") #returns 0, strange... 
*:错误消息:10053,软件导致连接中止

为什么我不能发送多封邮件?

在urllib2.py:1216中创建的urllib2中存在一个参考循环。 该问题持续存在,自2009年以来一直存在

使用您的链接建议的测试代码,我测试了我的Python安装并确认它确实泄漏了。 但是,如果按照@Russell的建议,如果我将每个urlopen放入其自己的进程中,则操作系统清除内存泄漏。 在我的测试中,内存,无法访问的对象和打开的文件都或多或少保持不变。 我将代码分成两个文件:

connection.py

import cPickle, urllib2

def connectFunction(queryString):
    conn = urllib2.urlopen('http://something.com/foo.php?parameter='+str(queryString))
    data = conn.read()
    outfile = ('sometempfile'. 'wb')
    cPickle.dump(data, outfile)
    outfile.close()

if __name__ == '__main__':
    connectFunction(sys.argv[1])

###launcher.py
import subprocess, cPickle

#code from your link to check the number of unreachable objects

def print_unreachable_len():
    # check memory on memory leaks
    import gc
    gc.set_debug(gc.DEBUG_SAVEALL)
    gc.collect()
    unreachableL = []

    for it in gc.garbage:
        unreachableL.append(it)
    return len(str(unreachableL))

    #my code
    if __name__ == '__main__':        
        print 'Before running a single process:', print_unreachable_len()
        return_value_list = []
        for i, value in enumerate(values): #where values is a list or a generator containing (or yielding) the parameters to pass to the URL
             subprocess.call(['python', 'connection.py', str(value)])
             print 'after running', i, 'processes:', print_unreachable_len()
             infile = open('sometempfile', 'rb')
             return_value_list.append(cPickle.load(infile))
             infile.close()

显然,这是顺序的,因此您一次只能执行一个连接,这对您来说可能会或可能不会成问题。 如果是这样,您将必须找到一种与您正在启动的进程进行通信的非阻塞方式,但是我将把它作为练习留给您。

编辑 :在重新阅读您的问题时,似乎您并不在乎服务器的响应。 在这种情况下,您可以摆脱所有与酸洗相关的代码。 很显然,您的最终代码中也不会包含与print_unreachable_len()相关的位。

这似乎是一个(非常!)骇人的解决方法,但在进行了一些谷歌搜索后发现了有关此问题的 :

显然添加f.read(1)将阻止泄漏!

import urllib2
f = urllib2.urlopen('http://www.google.com')
f.read(1)
f.close()

编辑 :哦,我看到您已经有了f.read(1) ...我当时全f.read(1)主意了:/

考虑使用低级 (相关的 )代替urllib2。

HOST = 'daring.cwi.nl'    # The remote host
PORT = 50007              # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send('GET /path/to/file/index.html HTTP/1.0\n\n')

 # you'll need to figure out how much data to read and read that exactly
 # or wait for read() to return data of zero length (I think!)
DATA_SZ = 1024
data    = s.recv(DATA_SZ)
s.close()
print 'Received', repr(data)

如何通过低级套接字执行和读取HTTP请求超出了该问题的范围(也许可以在stackoverflow上单独提出一个很好的问题-我进行了搜索,但没有看到它),但是我希望这样做为您指明可以解决您的问题的解决方案的方向!

编辑此处有关使用makefile的答案可能会有所帮助:

在Mac上使用Python 2.6.1时,这不会泄漏给我。 您正在使用哪个版本?

顺便说一句,由于输入错误,您的程序无法正常工作。 这是一个有效的方法:

import urllib2, httplib, gc
value = "foo"
count = 0
while(True):
    url = "http://192.168.1.1/?parameter=" + value 
    f = urllib2.urlopen(url)
    f.read(1)
    f.fp._sock.recv=None # hacky avoidance
    f.close()
    del f
    print "count=",count
    count += 1

取决于平台和python版本,python可能不会将内存释放回OS。 请参阅此 。 也就是说,python不应该无休止地消耗内存。 从您使用的代码来看, 除非 urllib / sockets使用我不相信的全局变量, 否则它似乎是python运行时中的错误-将其归咎于S60上的Python!

您是否考虑过其他内存泄漏源? 打开无休止的日志文件,像这样增加阵列或存储空间吗? 如果确实是套接字接口中的错误,那么您唯一的选择是使用子流程方法。

我认为可能是您的问题。 总结一下该线程,Pys60的DN??S查找中存在内存泄漏,您可以通过将DNS查找移到内部循环之外来解决此问题。