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

List:       pykde
Subject:    Re: [PyQt] Possible memory leak with signal connections
From:       "Kevin Keating" <kevin.keating () schrodinger ! com>
Date:       2019-09-25 21:05:43
Message-ID: 8b370174-9b43-4a03-8f24-8ed389056135 () getmailbird ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


On 9/25/2019 9:17:24 AM, Phil Thompson <phil@riverbankcomputing.com> wrote:
On 19/09/2019 19:53, Kevin Keating wrote:
> I've found what appears to be a memory leak caused by connecting
> signals to slots that lack a QtCore.pyqtSlot decorator.=C2=A0 I know that
> connections without the pyqtSlot decorator are expected to use more
> memory than connections with the decorator based on
> https://www.codeproject.com/Articles/1123088/PyQt-signal-slot-connection-=
performance.=C2=A0
> In the script I've pasted below, though, connections without the
> pyqtSlot decorator continue to consume memory even after the signal
> has been disconnected and the QObjects have been discarded.=C2=A0 The
> script does the following:
> =C2=A0 - Instantiates a bunch of SignalObjects, which are QObjects with a
> signal, and stores the SingalObjects in a list.
> =C2=A0 - Instantiates a bunch of SlotObjects, which are QObjects with
> slots.=C2=A0 Each SlotObject slot is connected to the signals from all the
> SignalObjects.=C2=A0 The SlotObjects are then immediately discarded.
> =C2=A0 - Discards all SignalObjects.
> =C2=A0 - Runs gc.collect()
> =C2=A0 - Runs the QApplication's event loop for a second in case there are
> any pending DeferredDelete events.
>
> If the SlotObject class uses the QtCore.pyqtSlot decorator, then
> memory usage at the end of the script is the same as what it was at
> the beginning of the script, which makes sense since all the objects
> that get created should be completely destroyed before the script
> finishes.=C2=A0 Here's the output that I get with pyqtSlot decorators:
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 Memory before signal_objects creation: 17.1MiB
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 Memory before slot objects creation: 17.1MiB
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Difference: 0.0B
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 Memory after slot objects creation: 17.1MiB
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Difference: 0.0B
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 Memory after event loop runs: 17.1MiB
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Difference: 0.0B
> If the SlotObject class doesn't use the QtCore.pyqtSlot decorator,
> though, then memory usage at the end of the script is substantially
> higher.=C2=A0 Here's the output that I get without pyqtSlot decorators:
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 Memory before signal_objects creation: 17.0MiB
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 Memory before slot objects creation: 17.0MiB
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Difference: 0.0B
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 Memory after slot objects creation: 210.6MiB
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Difference: 193.5=
MiB
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 Memory after event loop runs: 210.6MiB
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Difference: 193.5=
MiB
> Is this expected behavior?=C2=A0 Without pyqtSlot decorators, is there
> anything I can do to recover the 193 MiB of memory other than
> terminating the process?=C2=A0 Thanks!
>
> I've tested the script with Python 3.6.2, PyQt 5.12.2, and Qt 5.12.3
> on Windows 10, Linux, and Mac OS.=C2=A0 I've also tested with Python 3.6.=
5,
> PyQt 5.13.1, and Qt 5.13.1 on Windows 10.=C2=A0 All of them give similar
> results.=C2=A0 The script requires the psutil package
> (https://pypi.org/project/psutil/) to monitor memory usage.

Are you expecting that the Python interpreter will return unused memory
to the operating system?

Phil

My assumption was that the memory usage from the signal connections wouldn'=
t "build up" (i.e. that memory usage would be independent of the number of =
SignalObjects instantiated), which seems to be what happens with the pyqtSl=
ot decorated version.=C2=A0 In create_slot_objects, each SlotObject is inst=
antiated, which connects the signals, and then should be immediately garbag=
e collected, which should disconnect the signals.=C2=A0 In the version of t=
he script without the pyqtSlot decorators, the number reported for "Memory =
after slot objects creation" is proportional to the number of SlotObject in=
stances created, which is why I'd assumed memory had leaked, but I could ce=
rtainly be thinking through things wrong.

- Kevin
[Attachment #5 (text/html)]

<div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: \
#000000"><blockquote class="history_container" type="cite" style="border-left-style: \
solid;border-width: 1px;margin-top: 20px;margin-left: 0px;padding-left: \
10px;min-width: 500px"><p style="color: #AAAAAA; margin-top: 10px;">On 9/25/2019 \
9:17:24 AM, Phil Thompson &lt;phil@riverbankcomputing.com&gt; wrote:</p><div \
style="font-family:Arial,Helvetica,sans-serif">On 19/09/2019 19:53, Kevin Keating \
wrote: <br>&gt; I've found what appears to be a memory leak caused by connecting
<br>&gt; signals to slots that lack a QtCore.pyqtSlot decorator.&nbsp; I know that
<br>&gt; connections without the pyqtSlot decorator are expected to use more
<br>&gt; memory than connections with the decorator based on
<br>&gt; https://www.codeproject.com/Articles/1123088/PyQt-signal-slot-connection-performance.&nbsp;
 <br>&gt; In the script I've pasted below, though, connections without the
<br>&gt; pyqtSlot decorator continue to consume memory even after the signal
<br>&gt; has been disconnected and the QObjects have been discarded.&nbsp; The
<br>&gt; script does the following:
<br>&gt; &nbsp; - Instantiates a bunch of SignalObjects, which are QObjects with a
<br>&gt; signal, and stores the SingalObjects in a list.
<br>&gt; &nbsp; - Instantiates a bunch of SlotObjects, which are QObjects with
<br>&gt; slots.&nbsp; Each SlotObject slot is connected to the signals from all the
<br>&gt; SignalObjects.&nbsp; The SlotObjects are then immediately discarded.
<br>&gt; &nbsp; - Discards all SignalObjects.
<br>&gt; &nbsp; - Runs gc.collect()
<br>&gt; &nbsp; - Runs the QApplication's event loop for a second in case there are
<br>&gt; any pending DeferredDelete events.
<br>&gt; 
<br>&gt; If the SlotObject class uses the QtCore.pyqtSlot decorator, then
<br>&gt; memory usage at the end of the script is the same as what it was at
<br>&gt; the beginning of the script, which makes sense since all the objects
<br>&gt; that get created should be completely destroyed before the script
<br>&gt; finishes.&nbsp; Here's the output that I get with pyqtSlot decorators:
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; Memory before signal_objects creation: 17.1MiB
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; Memory before slot objects creation: 17.1MiB
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Difference: 0.0B
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; Memory after slot objects creation: 17.1MiB
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Difference: 0.0B
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; Memory after event loop runs: 17.1MiB
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Difference: 0.0B
<br>&gt; If the SlotObject class doesn't use the QtCore.pyqtSlot decorator,
<br>&gt; though, then memory usage at the end of the script is substantially
<br>&gt; higher.&nbsp; Here's the output that I get without pyqtSlot decorators:
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; Memory before signal_objects creation: 17.0MiB
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; Memory before slot objects creation: 17.0MiB
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Difference: 0.0B
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; Memory after slot objects creation: 210.6MiB
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Difference: 193.5MiB
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; Memory after event loop runs: 210.6MiB
<br>&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Difference: 193.5MiB
<br>&gt; Is this expected behavior?&nbsp; Without pyqtSlot decorators, is there
<br>&gt; anything I can do to recover the 193 MiB of memory other than
<br>&gt; terminating the process?&nbsp; Thanks!
<br>&gt; 
<br>&gt; I've tested the script with Python 3.6.2, PyQt 5.12.2, and Qt 5.12.3
<br>&gt; on Windows 10, Linux, and Mac OS.&nbsp; I've also tested with Python 3.6.5,
<br>&gt; PyQt 5.13.1, and Qt 5.13.1 on Windows 10.&nbsp; All of them give similar
<br>&gt; results.&nbsp; The script requires the psutil package
<br>&gt; (https://pypi.org/project/psutil/) to monitor memory usage.
<br>
<br>Are you expecting that the Python interpreter will return unused memory 
<br>to the operating system?
<br>
<br>Phil
</div></blockquote><div><span style="font-size: 10pt;font-family: Arial;color: \
#000000"><span style="color: #3B5998"><br></span></span>My assumption was that the \
memory usage from the signal connections wouldn't "build up" (i.e. that memory usage \
would be independent of the number of SignalObjects instantiated), which seems to be \
what happens with the pyqtSlot decorated version.&nbsp; In create_slot_objects, each \
SlotObject is instantiated, which connects the signals, and then should be \
immediately garbage collected, which should disconnect the signals.&nbsp; In the \
version of the script without the pyqtSlot decorators, the number reported for \
"Memory after slot objects creation" is proportional to the number of SlotObject \
instances created, which is why I'd assumed memory had leaked, but I could certainly \
be thinking through things wrong.</div><div><br></div><div>- Kevin</div></div>


[Attachment #6 (text/plain)]

_______________________________________________
PyQt mailing list    PyQt@riverbankcomputing.com
https://www.riverbankcomputing.com/mailman/listinfo/pyqt


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

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