Proper fix for switch library mem leak (I hope)

This commit is contained in:
Kovid Goyal 2011-01-22 13:54:45 -07:00
parent 57883c120e
commit 25a62ee96c
5 changed files with 45 additions and 14 deletions

View File

@ -384,14 +384,28 @@ class ChooseLibraryAction(InterfaceAction):
return return
prefs['library_path'] = loc prefs['library_path'] = loc
#from calibre.utils.mem import memory from calibre.utils.mem import memory
#import weakref, gc import weakref
#ref = weakref.ref(self.gui.library_view.model().db) from PyQt4.Qt import QTimer
#before = memory()/1024**2 self.dbref = weakref.ref(self.gui.library_view.model().db)
self.before_mem = memory()/1024**2
self.gui.library_moved(loc) self.gui.library_moved(loc)
#print gc.get_referrers(ref)[0] QTimer.singleShot(1000, self.debug_leak)
#for i in xrange(3): gc.collect()
#print 'leaked:', memory()/1024**2 - before def debug_leak(self):
import gc
from calibre.utils.mem import memory
ref = self.dbref
for i in xrange(3): gc.collect()
if ref() is not None:
print 11111, ref()
for r in gc.get_referrers(ref())[:10]:
print r
print
print 'before:', self.before_mem
print 'after:', memory()/1024**2
self.dbref = self.before_mem = None
def qs_requested(self, idx, *args): def qs_requested(self, idx, *args):
self.switch_requested(self.qs_locations[idx]) self.switch_requested(self.qs_locations[idx])

View File

@ -150,13 +150,13 @@ class GuiRunner(QObject):
if DEBUG: if DEBUG:
prints('Starting up...') prints('Starting up...')
def start_gui(self): def start_gui(self, db):
from calibre.gui2.ui import Main from calibre.gui2.ui import Main
main = Main(self.opts, gui_debug=self.gui_debug) main = Main(self.opts, gui_debug=self.gui_debug)
if self.splash_screen is not None: if self.splash_screen is not None:
self.splash_screen.showMessage(_('Initializing user interface...')) self.splash_screen.showMessage(_('Initializing user interface...'))
self.splash_screen.finish(main) self.splash_screen.finish(main)
main.initialize(self.library_path, self.db, self.listener, self.actions) main.initialize(self.library_path, db, self.listener, self.actions)
if DEBUG: if DEBUG:
prints('Started up in', time.time() - self.startup_time) prints('Started up in', time.time() - self.startup_time)
add_filesystem_book = partial(main.iactions['Add Books'].add_filesystem_book, allow_device=False) add_filesystem_book = partial(main.iactions['Add Books'].add_filesystem_book, allow_device=False)
@ -200,8 +200,7 @@ class GuiRunner(QObject):
det_msg=traceback.format_exc(), show=True) det_msg=traceback.format_exc(), show=True)
self.initialization_failed() self.initialization_failed()
self.db = db self.start_gui(db)
self.start_gui()
def initialize_db(self): def initialize_db(self):
db = None db = None

View File

@ -114,6 +114,9 @@ class TagsView(QTreeView): # {{{
def set_database(self, db, tag_match, sort_by): def set_database(self, db, tag_match, sort_by):
self.hidden_categories = config['tag_browser_hidden_categories'] self.hidden_categories = config['tag_browser_hidden_categories']
old = getattr(self, '_model', None)
if old is not None:
old.break_cycles()
self._model = TagsModel(db, parent=self, self._model = TagsModel(db, parent=self,
hidden_categories=self.hidden_categories, hidden_categories=self.hidden_categories,
search_restriction=None, search_restriction=None,
@ -371,6 +374,9 @@ class TagsView(QTreeView): # {{{
# model. Reason: it is much easier than reconstructing the browser tree. # model. Reason: it is much easier than reconstructing the browser tree.
def set_new_model(self, filter_categories_by=None): def set_new_model(self, filter_categories_by=None):
try: try:
old = getattr(self, '_model', None)
if old is not None:
old.break_cycles()
self._model = TagsModel(self.db, parent=self, self._model = TagsModel(self.db, parent=self,
hidden_categories=self.hidden_categories, hidden_categories=self.hidden_categories,
search_restriction=self.search_restriction, search_restriction=self.search_restriction,
@ -544,6 +550,9 @@ class TagsModel(QAbstractItemModel): # {{{
tooltip=tt, category_key=r) tooltip=tt, category_key=r)
self.refresh(data=data) self.refresh(data=data)
def break_cycles(self):
self.db = self.root_item = None
def mimeTypes(self): def mimeTypes(self):
return ["application/calibre+from_library"] return ["application/calibre+from_library"]
@ -1109,8 +1118,7 @@ class TagBrowserMixin(object): # {{{
def __init__(self, db): def __init__(self, db):
self.library_view.model().count_changed_signal.connect(self.tags_view.recount) self.library_view.model().count_changed_signal.connect(self.tags_view.recount)
self.tags_view.set_database(self.library_view.model().db, self.tags_view.set_database(db, self.tag_match, self.sort_by)
self.tag_match, self.sort_by)
self.tags_view.tags_marked.connect(self.search.set_search_string) self.tags_view.tags_marked.connect(self.search.set_search_string)
self.tags_view.tag_list_edit.connect(self.do_tags_list_edit) self.tags_view.tag_list_edit.connect(self.do_tags_list_edit)
self.tags_view.user_category_edit.connect(self.do_user_categories_edit) self.tags_view.user_category_edit.connect(self.do_user_categories_edit)

View File

@ -42,6 +42,9 @@ class MetadataBackup(Thread): # {{{
def stop(self): def stop(self):
self.keep_running = False self.keep_running = False
# Break cycles so that this object doesn't hold references to db
self.do_write = self.get_metadata_for_dump = self.clear_dirtied = \
self.set_dirtied = self.db = None
def run(self): def run(self):
while self.keep_running: while self.keep_running:
@ -185,6 +188,11 @@ class ResultCache(SearchQueryParser): # {{{
self.build_date_relop_dict() self.build_date_relop_dict()
self.build_numeric_relop_dict() self.build_numeric_relop_dict()
def break_cycles(self):
self._data = self.field_metadata = self.FIELD_MAP = \
self.numeric_search_relops = self.date_search_relops = \
self.all_search_locations = None
def __getitem__(self, row): def __getitem__(self, row):
return self._data[self._map_filtered[row]] return self._data[self._map_filtered[row]]

View File

@ -362,7 +362,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.last_update_check = self.last_modified() self.last_update_check = self.last_modified()
def break_cycles(self): def break_cycles(self):
self.data = self.field_metadata = self.prefs = self.listeners = None self.data.break_cycles()
self.data = self.field_metadata = self.prefs = self.listeners = \
self.refresh_ondevice = None
def initialize_database(self): def initialize_database(self):
metadata_sqlite = open(P('metadata_sqlite.sql'), 'rb').read() metadata_sqlite = open(P('metadata_sqlite.sql'), 'rb').read()