[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pykde
Subject:    [PyKDE] PyQT Memory Leak (new)
From:       Nahuel Greco <nahuel () puntorojo ! com ! ar>
Date:       2005-03-23 15:51:41
Message-ID: 20050323125141.03e3c0e5.nahuel () puntorojo ! com ! ar
[Download RAW message or body]

Hi, I think that I have found a memory leak in the latest PyQT snapshot. When you
create forms, if the form constructor connects their signals, after his destruction
a "weakref" object remains in memory. Note the attached logs:

   without_signal_connections.log: For this log I only pressed the second button,
   so the forms were created without connecting their signals. Python objects count
   remains constant, as you can see in the gc debug messages. Also the memory used
   don't increases.

   connecting_signals.log: Here I only pressed the first button, the forms were
   created connecting their signals. This case leaks, the memory increased after
   each run, and you can see in the gc debug messages that for each created form
   an object of type 'weakref' remains without beign collected.


Saludos,
Nahuel Greco.




Saludos,
Nahuel Greco.


["without_signal_connections.log" (application/octet-stream)]
["connecting_signals.log" (application/octet-stream)]
["isolate_new_leak.py" (text/x-python)]

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import sys, os, time, gc
from qt import *

sys.stderr = sys.stdout

######### Query Memory Usage Functions ####################################
def report(s):
    print s
    sys.stdout.flush()

def presentMemData(s=""):
    vmsize = vmrss = 0 
    for l in open('/proc/self/status','r').readlines():
        if l.startswith('VmSize:'):
            vmsize = int(l.split()[1])
        elif l.startswith('VmRSS:'):
            vmrss  = int(l.split()[1])
    report(s+"VmSize: %d Kb VmRss: %d Kb" % (vmsize,vmrss))
############################################################################

class SimpleForm(QDialog):
    def __init__(self, connect_signal=True):
        # Note, I'm using None as the parent
        QDialog.__init__(self, None)
       
        self.button = QPushButton(self, "Button")

        if connect_signal:
            self.connect(self.button, SIGNAL('clicked()'), self.buttonHandler)

    def buttonHandler(self):
        pass

                               
class Main(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.last_gc_types = None

        l = QVBoxLayout(self,3,3)

        self.button1 = QPushButton(self)
        self.button1.setText("1- Construct Forms connecting their signals")
        self.button2 = QPushButton(self)
        self.button2.setText("2- Construct Forms without connect their signals")
        self.lab = QLabel(self)
        self.lab.setText("(Check console output for Memory Usage information)")
        
        l.addWidget(self.button1)
        l.addWidget(self.button2)
        l.addWidget(self.lab)
        
        self.connect(self.button1, SIGNAL('clicked()'), self.button1Handler)
        self.connect(self.button2, SIGNAL('clicked()'), self.button2Handler)
        
    def button1Handler(self):
        self.construct(True)

    def button2Handler(self):
        self.construct(False)
        
    def construct(self, connect_signals):
        global app
        NUM_FORMS = 1000
       
        report("-- %s - Constructing %d SimpleForm's, connecting signals=%r" % \
               (time.strftime("%H:%M:%S"), NUM_FORMS, connect_signals))

        for x in xrange(NUM_FORMS):
            s = SimpleForm(connect_signals)
            #s.exec_loop()
            s = None

        #report("-- End Construction - Forcing GC with lowest thresholds")

        old_thresholds = gc.get_threshold()
        gc.set_debug(gc.DEBUG_LEAK | gc.DEBUG_STATS)
        # After this setting, gc.collect() will check all the generations
        gc.set_threshold(1,1,1)

        col = gc.collect()

        gc.set_threshold(*old_thresholds)
        gc.set_debug(0)            

        uncol = len(gc.garbage)
        if col or uncol:
            report("-- GC Collected: %d Uncollected: %d" % (col, uncol))

        # Count instances by type
        #lobj = None
        reference_counts_by_type = {}
        for obj in gc.get_objects():
            t = type(obj)
            if reference_counts_by_type.has_key(t):
                reference_counts_by_type[t] += 1
                lobj = obj
            else:
                reference_counts_by_type[t] = 1
        #if lobj:
        #    report("REFERRS: %r" %  gc.get_referrers(lobj))

        # Dont press the button too fast, this line is for ensuring that
        # the xlib memory is deallocated. 
        app.processEvents()

        # Print the types that now have more instances
        if self.last_gc_types:
            report("-- Types of python objects whose number of instances increased from last command:")
            for k,v in self.last_gc_types.items():
                if reference_counts_by_type.has_key(k) and reference_counts_by_type[k] > v:
                    report("\t%r instances count: %r (+%d)" % (k,v, reference_counts_by_type[k] - v ))


        self.last_gc_types = reference_counts_by_type
        presentMemData("-- Memory Usage: ")
        report("")


app = QApplication(sys.argv)
w = Main()
app.setMainWidget(w)
w.show()

report("QT Version:   "+ qVersion())
report("PYQT Version: "+ PYQT_VERSION_STR)
report("SIP Version:  "+ os.popen('sip -V').read())
presentMemData()
print

app.exec_loop()






_______________________________________________
PyKDE mailing list    PyKDE@mats.imk.fraunhofer.de
http://mats.imk.fraunhofer.de/mailman/listinfo/pykde


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic