Pyqt 4 - QWebView.load(url) утечки памяти (не из Python)
По сути, я извлекаю серию ссылок из своей базы данных и хочу просмотреть их для конкретных ссылок, которые я ищу. Затем я повторно отправляю эти ссылки в свою очередь ссылок, на которую ссылаются несколько моих QWebView, и они продолжают собирать их для обработки / хранения.
Моя проблема в том, что, поскольку это работает... скажем, на 200 или 500 ссылок, оно начинает использовать все больше и больше оперативной памяти.
Я подробно рассмотрел это, используя heapy, memory_profiler и objgraph, чтобы выяснить, что вызывает утечку памяти... Объекты кучи питона остаются примерно одинаковыми с точки зрения количества и размера с течением времени. Это заставило меня думать, что объекты C++ не удаляются. Конечно, используя memory_profiler, объем оперативной памяти увеличивается только при вызове строк кода self.load(self.url). Я пытался это исправить, но безрезультатно.
Код:
from PyQt4.QtCore import QUrl
from PyQt4.QtWebKit import QWebView, QWebSettings
from PyQt4.QtGui import QApplication
from lxml.etree import HTMLParser
# My functions
from util import dump_list2queue, parse_doc
class ThreadFlag:
def __init__(self, threads, jid, db):
self.threads = threads
self.job_id = jid
self.db_direct = db
self.xml_parser = HTMLParser()
class WebView(QWebView):
def __init__(self, thread_flag, id_no):
super(QWebView, self).__init__()
self.loadFinished.connect(self.handleLoadFinished)
self.settings().globalSettings().setAttribute(QWebSettings.AutoLoadImages, False)
# This is actually a dict with a few additional details about the url we want to pull
self.url = None
# doing one instance of this to avoid memory leaks
self.qurl = QUrl()
# id of the webview instance
self.id = id_no
# Status webview instance, green mean it isn't working and yellow means it is.
self.status = 'GREEN'
# Reference to a single universal object all the webview instances can see.
self.thread_flag = thread_flag
def handleLoadFinished(self):
try:
self.processCurrentPage()
except Exception as e:
print e
self.status = 'GREEN'
if not self.fetchNext():
# We're finished!
self.loadFinished.disconnect()
self.stop()
else:
# We're not finished! Do next url.
self.qurl.setUrl(self.url['url'])
self.load(self.qurl)
def processCurrentPage(self):
self.frame = str(self.page().mainFrame().toHtml().toUtf8())
# This is the case for the initial web pages I want to gather links from.
if 'name' in self.url:
# Parse html string for links I'm looking for.
new_links = parse_doc(self.thread_flag.xml_parser, self.url, self.frame)
if len(new_links) == 0: return 0
fkid = self.url['pkid']
new_links = map(lambda x: (fkid, x['title'],x['url'], self.thread_flag.job_id), new_links)
# Post links to database, db de-dupes and then repull ones that made it.
self.thread_flag.db_direct.post_links(new_links)
added_links = self.thread_flag.db_direct.get_links(self.thread_flag.job_id,fkid)
# Add the pulled links to central queue all the qwebviews pull from
dump_list2queue(added_links, self._urls)
del added_links
else:
# Process one of the links I pulled from the initial set of data that was originally in the queue.
print "Processing target link!"
# Get next url from the universal queue!
def fetchNext(self):
if self._urls and self._urls.empty():
self.status = 'GREEN'
return False
else:
self.status = 'YELLOW'
self.url = self._urls.get()
return True
def start(self, urls):
# This is where the reference to the universal queue gets made.
self._urls = urls
if self.fetchNext():
self.qurl.setUrl(self.url['url'])
self.load(self.qurl)
# uq = central url queue shared between webview instances
# ta = array of webview objects
# tf - thread flag (basically just a custom universal object that all the webviews can access).
# This main "program" is started by another script elsewhere.
def main_program(uq, ta, tf):
app = QApplication([])
webviews = ta
threadflag = tf
tf.app = app
print "Beginning the multiple async web calls..."
# Create n "threads" (really just webviews) that each will make asynchronous calls.
for n in range(0,threadflag.threads):
webviews.append(WebView(threadflag, n+1))
webviews[n].start(uq)
app.exec_()
Вот что говорят мои инструменты памяти (они все примерно одинаковы во всей программе)
- RAM: resource.getrusage (resource.RUSAGE_SELF).ru_maxrss / 1024
2491 (MB)
- Objgraph наиболее распространенных типов:
дескриптор метода 9959
функция 8342
слабый реф 6440
кортеж 6418
Дикт 4982
обертка_дескриптор 4380
getset_descriptor 2314
список 1890
method_descriptor 1445
встроенный_функция_метод 1298
- бесформенный:
Перегородка из набора 9879 предметов. Общий размер = 1510000 байт.
Индекс Количество% Размер% Совокупный% Вид (класс / диктат класса)
0 2646 27 445216 29 445216 29 str 1 563 6 262088 17 707304 47 dict (no owner) 2 2267 23 199496 13 906800 60 __builtin__.weakref 3 2381 24 179128 12 1085928 72 tuple 4 212 2 107744 7 1193672 79 dict of guppy.etc.Glue.Interface 5 50 1 52400 3 1246072 83 dict of guppy.etc.Glue.Share 6 121 1 40200 3 1286272 85 list 7 116 1 32480 2 1318752 87 dict of guppy.etc.Glue.Owner 8 240 2 30720 2 1349472 89 types.CodeType 9 42 0 24816 2 1374288 91 dict of class
1 ответ
Ваша программа действительно развивается из-за кода C++, но это не фактическая утечка с точки зрения создания объектов, на которые больше нет ссылок. По крайней мере частично происходит то, что ваш QWebView содержит QWebPage, который содержит QWebHistory(). Каждый раз, когда вы вызываете self.load, история становится немного длиннее.
Обратите внимание, что QWebHistory имеет функцию clear().
Документация доступна: http://pyqt.sourceforge.net/Docs/PyQt4/qwebview.html