Closed
Description
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]