簡體   English   中英

使用urrlib2時如何解決Python內存泄漏?

[英]How to solve Python memory leak when using urrlib2?

我正在嘗試為手機編寫一個簡單的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年以來一直存在。https://bugs.python.org/issue1208304

使用您的鏈接建議的測試代碼,我測試了我的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)主意了:/

考慮使用低級套接字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的答案可能會有所幫助: 使用python中的套接字的HTTP基本身份驗證

在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。 請參閱此stackoverflow線程 也就是說,python不應該無休止地消耗內存。 從您使用的代碼來看, 除非 urllib / sockets使用我不相信的全局變量, 否則它似乎是python運行時中的錯誤-將其歸咎於S60上的Python!

您是否考慮過其他內存泄漏源? 打開無休止的日志文件,像這樣增加陣列或存儲空間嗎? 如果確實是套接字接口中的錯誤,那么您唯一的選擇是使用子流程方法。

我認為可能是您的問題。 總結一下該線程,Pys60的DN​​S查找中存在內存泄漏,您可以通過將DNS查找移到內部循環之外來解決此問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM