I have encountered a potential memory leak in Tkinter that occurs when dynamically generated widgets are frequently updated. Specifically, the issue arises when tk.StringVar is used to display and update text in labels. When the displayed text is rapidly and frequently changed, the memory consumption steadily grows without being released.
Details:
The problem manifests in applications where widgets are dynamically created and destroyed while being continuously updated with new values. In my specific case, tk.StringVar objects are used to control the text of labels. Each label is dynamically generated in a loop, displayed, and then destroyed to make room for new labels.
Even though the widgets are properly destroyed, the memory usage remains high and continues to increase as the StringVar objects are updated frequently.
System Information:
Operating System: Windows 10 (64-bit)
Python Version: 3.10.0 (in a Conda environment)
Tkinter Version: Standard version shipped with Python 3.10
Minimal Reproduction Example
Here is a minimal Python script that reliably reproduces the issue:
Monitor the application's memory usage over time. You should observe a steady increase in memory consumption, even though the dynamically generated widgets are being destroyed and removed.
Expected Behavior:
The memory should remain stable when widgets are dynamically removed and recreated.
Actual Behavior:
The memory usage continuously increases, indicating a potential memory leak.
Possible Cause:
It appears that tk.StringVar objects, when frequently updated and dynamically assigned, are not being properly released when the widgets are destroyed.
Additional Notes:
The issue is more pronounced when updates happen in very short intervals (e.g., every 50 milliseconds) while widgets are being dynamically created and destroyed.
I haven't had a chance to try and reproduce this, but at a glance, could this be related to a reference cycle? Add a gc.collect between iterations to trigger a collection cycle, and see if it still occurs.
When proc returns 1, Tcl_ServiceEvent will remove the event from the event queue and free its storage. Note that the storage for an event must be allocated by the event source (using Tcl_Alloc or the Tcl macro ckalloc) before calling Tcl_QueueEvent, but it will be freed by Tcl_ServiceEvent, not by the event source.
Threaded applications work in a similar manner, except that there is a separate event queue for each thread containing a Tcl interpreter
In addition, the other occurrences of using Tkapp_ThreadSend seems to suggest that the ev pointer is no more owned after the call.
@picnixz were you able to reproduce this? I haven't been able to. Memray is showing some leaks on false positives (e.g. the mainloop call), but StringVar isn't even showing up on the flamegraph. Likewise, htop doesn't show any memory increase over time, it stays continuous as expected.
@OberstMoeb how long were you running it for? I only ran it for a minute or two (generally, that's more than enough to see a leak, but maybe not in this case.)
A speculative guess could be that Windows uses lazy memory deallocation. It knows that the memory is freed, but since you're going to request some memory again later, it doesn't bother actually letting other processes use it, and just keeps it available in the current process, so the memory remains "unfreed" until the end the process ends.
tkinter isn't using Python's special allocator (as shown above), so objects aren't reused, and hence it looks like the memory is increasing over time. I'm not a Windows expert though; please correct me if I'm wrong!
This is expected behavior rather than a leak in tkinter itself: each Variable (StringVar, etc.) is backed by a Tcl variable that lives until the Python Variable object is deleted. Creating a fresh Variable for every update accumulates them; reuse a single Variable and update its value (or drop references to the ones you no longer need) instead.
The Variable lifetime is now documented in #152625, so I'm closing this as documented. Thanks for the report!
Activity
ZeroIntensity commented on Aug 21, 2024
I haven't had a chance to try and reproduce this, but at a glance, could this be related to a reference cycle? Add a gc.collect between iterations to trigger a collection cycle, and see if it still occurs.
terryjreedy commented on Aug 21, 2024
To do anything, we would have to know if the leak is in python (_tkinter) or tcl/tk (or somehow both?). If tcl/tk only, report on their tracker.
ZeroIntensity commented on Aug 22, 2024
I can try to run this with a memory profiler sometime later today, but I do see a potential candidate for the problem.
cpython/Modules/_tkinter.c
Line 1755 in 3d7b1a5
AFAICS, ev is not deallocated (unless Tcl_ThreadQueueEvent does it, which would be odd.)
picnixz commented on Aug 22, 2024
I think this is the case (see https://linux.die.net/man/3/tcl_threadqueueevent) (emphasis mine):
In addition, the other occurrences of using Tkapp_ThreadSend seems to suggest that the ev pointer is no more owned after the call.
ZeroIntensity commented on Aug 22, 2024
@picnixz were you able to reproduce this? I haven't been able to. Memray is showing some leaks on false positives (e.g. the mainloop call), but StringVar isn't even showing up on the flamegraph. Likewise, htop doesn't show any memory increase over time, it stays continuous as expected.
@OberstMoeb how long were you running it for? I only ran it for a minute or two (generally, that's more than enough to see a leak, but maybe not in this case.)
OberstMoeb commented on Aug 22, 2024
The leak occurs for me after 10 second. May it is a problem, which is only coming up on windows.
ZeroIntensity commented on Aug 22, 2024
A speculative guess could be that Windows uses lazy memory deallocation. It knows that the memory is freed, but since you're going to request some memory again later, it doesn't bother actually letting other processes use it, and just keeps it available in the current process, so the memory remains "unfreed" until the end the process ends.
tkinter isn't using Python's special allocator (as shown above), so objects aren't reused, and hence it looks like the memory is increasing over time. I'm not a Windows expert though; please correct me if I'm wrong!
picnixz commented on Aug 23, 2024
No because I was too lazy and too busy for that. I was chiming in for the tcl details.
gh-123193: Document tkinter Variable lifetime (GH-152625)
[3.13] gh-123193: Document tkinter Variable lifetime (GH-152625) (GH-…
[3.14] gh-123193: Document tkinter Variable lifetime (GH-152625) (GH-…
[3.15] gh-123193: Document tkinter Variable lifetime (GH-152625) (GH-…
serhiy-storchaka commented on Jul 5, 2026
This is expected behavior rather than a leak in tkinter itself: each Variable (StringVar, etc.) is backed by a Tcl variable that lives until the Python Variable object is deleted. Creating a fresh Variable for every update accumulates them; reuse a single Variable and update its value (or drop references to the ones you no longer need) instead.
The Variable lifetime is now documented in #152625, so I'm closing this as documented. Thanks for the report!