8000 Memory leak with CallbackRegistry · Issue #19474 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content
Memory leak with CallbackRegistry #19474
Closed
@vallsv

Description

@vallsv

Hi,

Here is a report with a possible patch.

Bug report

We had memory leak issue with our library (see silx-kit/silx#3372).

CallbackRegistry can sometimes not clean up stored weakref from released object.

Bug summary

_func_cid_map is only cleaned up if a callback name if fully empty.

We can think of cases where objects are released in order to never make this callback empty.

As result dead weakrefs are still stored in the structure.

Code for reproduction

from matplotlib.cbook import CallbackRegistry

callbacks = CallbackRegistry()

class Foo:
    def callback(x):
        pass

for i in range(100):
    a = Foo()
    callbacks.connect("units", a.callback)
    b = Foo()
    callbacks.connect("units", b.callback)

    print("Is that grow up?", len(callbacks._func_cid_map['units']))

Expected outcome

We expect _func_cid_map to be not bigger than 2.

Matplotlib version

  • 3.3.4
  • git-blame tells it was there 6 years ago

Fix

Without thinking much, i did this hotfix.

     def _remove_proxy(self, proxy, *, _is_finalizing=sys.is_finalizing):
         if _is_finalizing():
             # Weakrefs can't be properly torn down at that point anymore.
             return
         for signal, proxies in list(self._func_cid_map.items()):
             try:
                 del self.callbacks[signal][proxies[proxy]]
+                del self._func_cid_map[signal][proxy]
             except KeyError:
                 pass
             if len(self.callbacks[signal]) == 0:
                 del self.callbacks[signal]
                 del self._func_cid_map[signal]

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0