flet: possible memory leak

Memory leak on simple application

The attached app, appears to leak memory consistently as tabs are opened and closed to view the page. In my tests the memory went up from 40.1MB to 40.9MB after opening and closing 20 or 30 tabs. In my production app it is in the 10’s of megabytes.

You need to install memory_profiler to see the increased usage, the increase is small on only Text elements (for instance) but adding ElevatedButton appears to make the leak bigger.

I have left the app running for hours to test if the GC collects this memory later, but it does not appear to.

Code example to reproduce the issue:

import logging
from memory_profiler import profile

import flet as ft


import flet as ft


@profile(precision=6)
def on_connect(e):
    logging.debug("Connected")


@profile(precision=6)
def on_disconnect(e):
    logging.debug("Disconnected")

@profile(precision=6)
def on_click(e):
    print( "Clicked" )

@profile(precision=6)
def main(page: ft.Page):
    page.on_connect = on_connect
    page.on_disconnect = on_disconnect
    page.controls.append(ft.Text(value="Hello, world!", color="green"))
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.Text(value="Hello, world!", color="green"))
    page.controls.append(ft.Text(value="Hello, world!", color="green"))
    page.controls.append(ft.Text(value="Hello, world!", color="green"))
    page.update()



ft.app(target=main, view=ft.WEB_BROWSER)

This is the memory dump on first connect:

Line #    Mem usage    Increment  Occurrences   Line Contents
=============================================================
    23  40.140625 MiB  40.140625 MiB           1   @profile(precision=6)
    24                                         def main(page: ft.Page):
    25  40.140625 MiB   0.000000 MiB           1       page.on_connect = on_connect
    26  40.140625 MiB   0.000000 MiB           1       page.on_disconnect = on_disconnect
    27  40.140625 MiB   0.000000 MiB           1       page.controls.append(ft.Text(value="Hello, world!", color="green"))
    28  40.140625 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    29  40.140625 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    30  40.140625 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    31  40.140625 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    32  40.140625 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    33  40.140625 MiB   0.000000 MiB           1       page.controls.append(ft.Text(value="Hello, world!", color="green"))
    34  40.140625 MiB   0.000000 MiB           1       page.controls.append(ft.Text(value="Hello, world!", color="green"))
    35  40.140625 MiB   0.000000 MiB           1       page.controls.append(ft.Text(value="Hello, world!", color="green"))
    36  40.140625 MiB   0.000000 MiB           1       page.update()

This is the memory dump 20 or 30 tabs later (all opened tabs are closed and a single connection is made again):

Line #    Mem usage    Increment  Occurrences   Line Contents
=============================================================
    23  40.906250 MiB  40.906250 MiB           1   @profile(precision=6)
    24                                         def main(page: ft.Page):
    25  40.906250 MiB   0.000000 MiB           1       page.on_connect = on_connect
    26  40.906250 MiB   0.000000 MiB           1       page.on_disconnect = on_disconnect
    27  40.906250 MiB   0.000000 MiB           1       page.controls.append(ft.Text(value="Hello, world!", color="green"))
    28  40.906250 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    29  40.906250 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    30  40.906250 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    31  40.906250 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    32  40.906250 MiB   0.000000 MiB           1       page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    33  40.906250 MiB   0.000000 MiB           1       page.controls.append(ft.Text(value="Hello, world!", color="green"))
    34  40.906250 MiB   0.000000 MiB           1       page.controls.append(ft.Text(value="Hello, world!", color="green"))
    35  40.921875 MiB   0.015625 MiB           1       page.controls.append(ft.Text(value="Hello, world!", color="green"))
    36  40.921875 MiB   0.000000 MiB           1       page.update()

Describe the results you expected: Expected memory to climb on new tab but to drop to original levels on close tab.

Additional information you deem important (e.g. issue happens only occasionally):

Flet version (pip show flet):

Name: flet
Version: 0.3.2
Summary: Flet for Python - easily build interactive multi-platform apps in Python
Home-page: 
Author: 
Author-email: Appveyor Systems Inc. <hello@flet.dev>
License: MIT
Location: /Users/gregor.brandt/Documents/Startbridge/web-app2/fe_env/lib/python3.11/site-packages
Requires: beartype, oauthlib, packaging, repath, requests, watchdog, websocket-client
Required-by: 

Operating system:

MacOS 13.1, using Firefox as browser

Additional environment details:

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 1
  • Comments: 15 (7 by maintainers)

Commits related to this issue

Most upvoted comments

To verify that memory is released when a user session is “closed” I used the following application:

import logging

import flet as ft
from memory_profiler import profile

@profile(precision=6)
def on_connect(e):
    logging.debug("Connected")

@profile(precision=6)
def on_disconnect(e):
    logging.debug("Disconnected")

@profile(precision=6)
def on_close(e):
    logging.debug("Closed")

@profile(precision=6)
def on_click(e):
    print("Clicked")

@profile(precision=6)
def main(page: ft.Page):
    page.on_connect = on_connect
    page.on_disconnect = on_disconnect
    page.on_close = on_close
    page.controls.append(
        ft.Text(data=f"a" * (1024 * 1024 * 128), value="Hello, world!", color="green")
    )
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.ElevatedButton("Click me", on_click=on_click))
    page.controls.append(ft.Text(value="Hello, world!", color="green"))
    page.controls.append(ft.Text(value="Hello, world!", color="green"))
    page.controls.append(ft.Text(value="Hello, world!", color="green"))
    page.update()

ft.app(target=main, view=ft.WEB_BROWSER)

The trick there is Text.data value which “eats” 128 MB of RAM for each user session.

Additionally, I set session lifetime to 1 minute via environment variable:

export FLET_APP_LIFETIME_MINUTES=1

Running the app and opening 5 tabs I can see python process takes 669 MB:

image

Closing all 5 tabs and waiting for 1 minute I can observe “close” event handler called for all 5 sessions:

python process takes 29 MB (669 - 128 * 5):

image

I’ve managed to fix it! Will be in tomorrow’s release.