8

我正在尝试为我的手机编写一个简单的 Python 脚本,以使用 urrlib2 定期加载网页。实际上我并不真正关心服务器响应,我只想将 URL 中的一些值传递给 PHP。问题是 Python for S60 使用旧的 2.5.4 Python 内核,这似乎在 urrlib2 模块中存在内存泄漏。正如我所读到的,在每种类型的网络通信中似乎也存在这样的问题。此处已报告此错误几年前,同时也发布了一些解决方法。在 Google 的帮助下,我已经尝试了在该页面上可以找到的所有内容,但在加载大约 70 个页面后,我的手机仍然内存不足。奇怪的是,垃圾收集器似乎也没有任何区别,除了让我的脚本慢得多。据说,较新的(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: 按照前面的建议尝试了套接字方法,这是迄今为止第二好的(错误)解决方案:


class UpdateSocketThread(threading.Thread):
  def run(self):
  global data
  while 1:
  url = "/foo.php?parameter=%d"%data
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect(('something.com', 80))
  s.send('GET '+url+' HTTP/1.0\r\n\r\n')
  s.close()
  sleep(1)
我也从上面尝试了这些小技巧。大约 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,软件导致连接中止

为什么我不能发送多条消息??

4

7 回答 7

1

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

连接.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()相关位。

于 2010-11-19T16:06:57.603 回答
1

在 urllib2.py:1216 中创建的 urllib2 中存在引用循环。该问题自 2009 年以来一直存在并存在。 https://bugs.python.org/issue1208304

于 2016-12-22T10:15:24.537 回答
0

这似乎是一个(非常!)hacky 解决方法,但是通过谷歌搜索发现了这个问题的评论

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

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

编辑:哦,我看到你已经有了f.read(1)......然后我完全没有想法了:/

于 2010-11-18T11:37:20.360 回答
0

考虑使用低级套接字 API(相关howto)而不是 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可能会有所帮助:HTTP basic authentication using sockets in python

于 2010-11-19T14:57:23.603 回答
0

这不会在 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
于 2010-11-21T13:07:38.567 回答
0

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

您是否考虑过其他内存泄漏源?无休止的日志文件打开,不断增加数组或类似的东西?如果它确实是套接字接口中的错误,那么您唯一的选择是使用子进程方法。

于 2010-11-22T12:50:17.947 回答
0

我想可能是你的问题。总而言之,Pys60 的 DNS 查找存在内存泄漏,您可以通过将 DNS 查找移到内部循环之外来解决它。

于 2011-01-15T02:43:33.557 回答