كيفية حل تسرب الذاكرة Python عند استخدام urrlib2؟
-
26-09-2019 - |
سؤال
أحاول كتابة نص Python بسيط لهاتفي المحمول لتحميل صفحة ويب بشكل دوري باستخدام URRLIB2. في الواقع ، لا أهتم حقًا باستجابة الخادم ، أود فقط تمرير بعض القيم في عنوان URL إلى PHP. المشكلة هي أن Python for S60 يستخدم Core 2.5.4 القديم ، الذي يبدو أنه يحتوي على تسرب ذاكرة في وحدة URRLIB2. كما قرأت ، يبدو أن هناك مثل هذه المشكلات في كل أنواع اتصالات الشبكة أيضًا. تم الإبلاغ عن هذا الخطأ هنا قبل عامين ، بينما تم نشر بعض الحلول أيضًا. لقد جربت كل ما يمكنني العثور عليه في تلك الصفحة ، وبمساعدة Google ، لكن هاتفي لا يزال ينفد من الذاكرة بعد ~ 70 صفحة. من الغريب أن جامع Garbege لا يبدو أنه يحدث أي فرق أيضًا ، باستثناء جعل البرنامج النصي أبطأ بكثير. يقال ، أن الأساس الأحدث (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 تحميلًا (يحتوي الهاتف على 50 ميجابايت من الذاكرة ، ومن الواضح أن قذيفة Python لم تكن كذلك.)
تحديث: أعتقد أنني أقترب من الحل! حاولت إرسال بيانات متعددة دون إغلاق المقبس وإعادة فتحه. قد يكون هذا هو المفتاح لأن هذه الطريقة ستترك فقط واصف ملف مفتوح واحد. المشكلة هي:
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 ، تسبب البرنامج في إحباط الاتصال
لماذا لا يمكنني إرسال رسائل متعددة ؟؟
المحلول
أظن هذه ربما مشكلتك. لتلخيص هذا الخيط ، هناك تسرب للذاكرة في البحث عن DNS من PYS60 ، ويمكنك العمل حوله عن طريق نقل البحث عن DNS خارج الحلقة الداخلية.
نصائح أخرى
باستخدام رمز الاختبار الذي اقترحه الرابط الخاص بك ، اختبرت تثبيت 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()
البتات ذات الصلة في الكود النهائي الخاص بك أيضا.
توجد دورة مرجعية في urllib2 التي تم إنشاؤها في urllib2.py:1216. القضية على الذهاب ووجهة منذ عام 2009.https://bugs.python.org/issue1208304
هذا يبدو وكأنه (جدا!) الحل البديل ، ولكن قليلا من googling هذا التعليق على المشكلة:
مضيفا على ما يبدو f.read(1)
سيوقف التسرب!
import urllib2
f = urllib2.urlopen('http://www.google.com')
f.read(1)
f.close()
تعديل: أوه ، أراك بالفعل f.read(1)
... أنا كل الأفكار ثم:/
النظر في استخدام المستوى المنخفض مقبس API (ذات صلة كيف) بدلا من 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 الأساسية باستخدام مآخذ في بيثون
هذا لا يتسرب بالنسبة لي مع بيثون 2.6.1 على جهاز Mac. ما هو الإصدار الذي تستخدمه؟
راجع للشغل ، لا يعمل برنامجك بسبب بعض الأخطاء المطبعية. هذا واحد يعمل:
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. ومع ذلك ، يجب ألا يستهلك بيثون الذاكرة إلى ما لا نهاية. انطلاقًا من الكود الذي تستخدمه ، يبدو أنه خطأ في وقت تشغيل بيثون ما لم, ، يستخدم Urllib/Sockets الكرات التي لا أعتقد أنها تفعل ذلك - ألومها على Python على S60!
هل فكرت في مصادر أخرى لتسرب الذاكرة؟ ملف السجل الذي لا نهاية له مفتوح ، صفيف متزايد أو SMTH من هذا القبيل؟ إذا كان ذلك حقًا خطأ في واجهة Sockets ، فإن خيارك الوحيد هو استخدام نهج العملية الفرعية.