GTK Image Memory Leak
- Dominant language
- Python
- Stars
- 5.4k
- Forks
- 827
- Avg merge
- 15h 39m
- Merged PRs (30d)
- 40
Description
### Describe the bug
GTK backend's Image implementation leaks memory even after GC on the Python side. When I repeatedly create Image instances under GTK backend and explicitly call gc.collect, memory usage still increases each time I create a new batch of instances.
### How to reproduce the bug
1. Save a large image (say an old movie poster) to `large_image.png` in the current directory
2. Create the file in the minimum example code section.
3. Run the file, and click the "Load 100 images" button 2-3 times
4. Watch the memory usage grow between each press
### Minimum example code
```shell
import asyncio
import os
import urllib.request
import psutil
import toga
import gc
from toga.constants import COLUMN
from toga.style import Pack
class WebMemoryLeakMonitor(toga.App):
def startup(self):
self.process = psutil.Process(os.getpid())
self.main_window = toga.MainWindow()
main_box = toga.Box(style=Pack(direction=COLUMN, padding=15))
self.memory_label = toga.Label(
f"Current RAM Usage: {self.get_memory_string()}",
style=Pack(padding_bottom=15)
)
self.load_button = toga.Button(
"Load 100 Images",
on_press=self.load_images_batch,
style=Pack(padding_bottom=10)
)
self.image_view = toga.ImageView(flex=1)
main_box.add(self.memory_label)
main_box.add(self.load_button)
main_box.add(self.image_view)
self.main_window.content = main_box
self.main_window.show()
def get_memory_string(self):
mem_bytes = self.process.memory_info().rss
return f"{mem_bytes / (1024 * 1024):.2f} MB"
def load_images_batch(self, widget):
for _ in range(100):
new_image = toga.Image(src="large_image.png")
self.image_view.image = new_image
for _ in range(10):
gc.collect()
self.memory_label.text = f"Current RAM Usage: {self.get_memory_string()}"
if __name__ == "__main__":
app = WebMemoryLeakMonitor("Web Leak Monitor", "org.example.web-leak")
app.main_loop()
```
### Screenshots
Eventually this type of thing will happen:
### Environment details
- Ubuntu 25.04
- Python version: 3.13.3
- Software versions:
- Toga: https://github.com/beeware/toga/commit/d3425374f029a174083464d8f448af299ed935a3
- GTK 3.24.49
### Logs
_No response_
### Additional context
- Same reproducer runs but bug does not occur on macOS.
- The issue may be caused by the use of Gio.MemoryInputStream.new_from_data: https://stackoverflow.com/questions/45838863/gio-memoryinputstream-does-not-free-memory-when-closed
https://github.com/beeware/toga/blob/d3425374f029a174083464d8f448af299ed935a3/gtk/src/toga_gtk/images.py#L15C32-L15C67
Switching to new_from_bytes, as suggested by the StackOverflow answer, may fix this issue.
- AI disclosure: Found this bug with the assistance of Gemini by asking it to find bugs in a GUI codebase and pasting in random segments of Toga it asks for. i can confirm manually that this is a legitmate issue.
Contributor guide
Research direction
Start by reading gtk/src/toga_gtk/images.py at the linked MemoryInputStream call, then run the supplied GTK reproducer with a large_image.png and monitor memory across repeated batches. Investigate the suggested new_from_bytes alternative; done means repeated image loading no longer shows the reported memory growth while GTK image display still works.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100