KivyMD: Potential memory leak when adding and deleting dynamic widgets
Description of the Bug
First off, I want to say thank you to all the developers who spent their time working on KivyMD. I have used it many times for work and pleasure. It really simplifies simple UI development.
Now for my issue. I am working on a large KivyMD project that involves lots of screens and adding and deleting dynamic widgets from said screens. I noticed that every time I add and clear widgets, that the system memory usage increases. I have let the program run to the point where it will use gigabytes of memory and start eating into the paging/swap file of the computer it is running on.
I have tested this issue on several different configurations and they all suffer from from infinitely increasing RAM usage
- Windows 10, Ubuntu 20.04, Ubuntu 22.04
- Python 3.8.8, Python 3.10.4
- KivyMD 1.0.2, KivyMD 1.0.0dev0
- Kivy 2.0.0, Kivy 2.1.0
I looked into some memory profiling tools(Guppy, GC…), but I am not skilled enough in the underlying memory management of python to understand how to troubleshoot it further. I also tried manual garbage collection using gc.collect() in various places in the program and the memory was still not relinquished back to the OS.
My current theory is that calls to self.clear_widgets() are deleting the UI elements from the screen just fine, but there are references to these widgets that are not being destroyed, causing python to never actually release the memory. This problem seems to occur only with KivyMD. I tried running an almost identical program with vanilla Kivy and the memory management issues are not present.
I have two different code snippets below:
- kivymd_test.py: This is the KivyMD test program. Run this code and open up Task Manager(System Monitor for Ubuntu) and observe as the RAM usage continuously increases until the program is killed.
- kivy_test.py: This is the vanilla Kivy test program. Run this code and you will see that memory is increased for awhile and then eventually memory is given back to the OS. This code seems to have a memory “plateau”. Where the memory usage will climb to a fixed point and not increase after.
Code and Logs
kivymd_test.py
from kivymd.app import MDApp
from kivymd.uix.button import MDRectangleFlatButton
from kivymd.uix.label import MDLabel
from kivy.clock import Clock
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.textfield import MDTextField
counter = 0
class MainApp(MDApp):
def build(self):
#Add MDFloatLayout as root widget
layout = MDFloatLayout()
#Schedule add_items() function to be called every second
self.clock_obj = Clock.schedule_interval(self.add_items, 1)
return layout
def add_items(self, dt):
#Increment global counter
global counter
counter += 1
#Clear all widgets
self.root.clear_widgets()
label = MDLabel(text="Count " + str(counter),
pos_hint={"x": 0.5, "top": 0.5}, size_hint=(.1, .1))
button= MDRectangleFlatButton(text="Count " + str(counter),
pos_hint={"x": 0.5, "top": 0.6},
size_hint=(.1, .1))
field = MDTextField(hint_text="Count " + str(counter), mode="fill",
pos_hint={"x": 0.5, "top": 0.7},
size_hint=(.1, .1))
#Add all widgets to parent layout
self.root.add_widget(label)
self.root.add_widget(button)
self.root.add_widget(field)
return
MainApp().run()
kivy_test.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
counter = 0
class MainApp(App):
def build(self):
#Add FloatLayout as root widget
layout = FloatLayout()
#Schedule add_items() function to be called every second
self.clock_obj = Clock.schedule_interval(self.add_items, 0.1)
return layout
def add_items(self, dt):
#Increment global counter
global counter
counter += 1
#Clear all widgets
self.root.clear_widgets()
label = Label(text="Count " + str(counter),
pos_hint={"x": 0.5, "top": 0.5}, size_hint=(.1, .1))
button= Button(text="Count " + str(counter),
pos_hint={"x": 0.5, "top": 0.6},
size_hint=(.1, .1))
field = TextInput(hint_text="Count " + str(counter),
pos_hint={"x": 0.5, "top": 0.7},
size_hint=(.1, .1))
#Add all widgets to parent layout
self.root.add_widget(label)
self.root.add_widget(button)
self.root.add_widget(field)
return
MainApp().run()
Versions
- Windows 10, Ubuntu 20.04, Ubuntu 22.04
- Python 3.8.8, Python 3.10.4
- KivyMD 1.0.2, KivyMD 1.0.0dev0
- Kivy 2.0.0, Kivy 2.1.0
Closing Remarks
I opened a Stack Overflow question about this issue to see if was something obvious I was missing, but it does not appear to be the case. I also think it is a bug, because an almost identical vanilla Kivy program does not suffer from this issue. Please let me know if I am missing any information. I would also be willing to help troubleshoot the issue further. Thank you for your consideration.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 50 (28 by maintainers)
Commits related to this issue
- Fix https://github.com/kivymd/KivyMD/issues/1332 Fix https://github.com/kivymd/KivyMD/pull/740 — committed to kivymd/KivyMD by HeaTTheatR 2 years ago
@bryant-saunders The problem is binding properties to methods. Links are not cleared.
This PR tried to solve this problem - https://github.com/kivymd/KivyMD/pull/740/commits/3b94b19b0616fcf5b65ee583abe64856c2ade317 But I no longer remember why these changes were removed from the master branch…
@bryant-saunders