You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The issue is that you are never letting the GUI event loop run so there are a growing number of GUI windows just hanging out in the background waiting for the event loop to run so they can finish their tear down cycle (because most UI frameworks have a way to notify that things are being destroyed etc).
If you need to run tight loops like this either use a non-interactive backend (like 'agg' or 'svg') which will never create the GUI objects or call plt.pause(.1) every so often to let the GUI clean up.
Can you please try adding gc.collect() (to clean up an reference cycles) and put the pause before the close (some of the backends do not actually run the event loop if the all of the managers are gone).
That long delay at the end is probably GC finally running on the way out
plt.close() is no effect in that second example because the Figure was never registered with
I understand the problem, TL;DR either use manual gc.collect() or set gc.set_threshold(100) because we internally use gc.collect(1) which breaks CPyhons garbage collection (I'll be opening up a PR soon).
a details that is not included is that the logic to sort out which generation to run is only run if the threshold for the lowest generation is met (cpython source)
if we do not have a code path between the plt.close() that does not make 700 objects then we never pass that first test and CPython never tries to run its "what generation to run?" logic (cpython source) and we have this run-away "leak"
You can reproduce this issue with:
importgcimportmatplotlib.figureasmfigureprint("about to start")
forjinrange(100_000):
fig=mfigure.Figure()
gc.collect(1)
print(gc.get_count())
Activity
nschloe commentedon Aug 22, 2022
The following is with
timhoffm commentedon Aug 22, 2022
Have you tried other backends? Is this GTK and/or AGG specific? Interesting would be "qtagg", "Tkagg", "agg" and "gtk4cairo".
tacaswell commentedon Aug 22, 2022
The issue is that you are never letting the GUI event loop run so there are a growing number of GUI windows just hanging out in the background waiting for the event loop to run so they can finish their tear down cycle (because most UI frameworks have a way to notify that things are being destroyed etc).
If you need to run tight loops like this either use a non-interactive backend (like
'agg'or'svg') which will never create the GUI objects or callplt.pause(.1)every so often to let the GUI clean up.nschloe commentedon Aug 22, 2022
I've tried the suggestion. Outcome: All backends have the problem, but
'agg'and'svg'run a lot faster than the rest. I've tried'agg''gtk3agg''gtk3cairo''gtk4agg''gtk4cairo''qtagg''qtcairo''svg'When adding
plt.pause(1.0)(a full second), no clean-up appears to happen. This is with'agg'; observe the plateaus:Code:
tacaswell commentedon Aug 22, 2022
Can you please try adding
gc.collect()(to clean up an reference cycles) and put the pause before the close (some of the backends do not actually run the event loop if the all of the managers are gone).Can you also try with https://github.com/matplotlib/matplotlib/blob/main/tools/memleak.py on your system?
This stays under 90MB for me (where as with out the
gc.collect()it did indeed start to run away).tacaswell commentedon Aug 22, 2022
Also seems to work so likely something in the
Managerclassnschloe commentedon Aug 22, 2022
agg without gc (100k iterations):
agg with gc (100k iterations, gc after every 1000 its):
nschloe commentedon Aug 22, 2022
nschloe commentedon Aug 22, 2022
Indeed:
gives
-- no explicit gc.
tacaswell commentedon Aug 23, 2022
That long delay at the end is probably GC finally running on the way out
plt.close()is no effect in that second example because theFigurewas never registered withI understand the problem, TL;DR either use manual
gc.collect()or setgc.set_threshold(100)because we internally usegc.collect(1)which breaks CPyhons garbage collection (I'll be opening up a PR soon).gc.collect(1)inmatplotlib/lib/matplotlib/_pylab_helpers.py
Lines 69 to 72 in 5773bf9
plt.close()that does not make 700 objects then we never pass that first test and CPython never tries to run its "what generation to run?" logic (cpython source) and we have this run-away "leak"You can reproduce this issue with:
A way to "fix" this is
which will force enough generation 0 objects to be created that it will cause a full gc eventually.
FIX: do not try to help CPython with garbage collection
FIX: do not try to help CPython with garbage collection
FIX: do not try to help CPython with garbage collection
FIX: do not try to help CPython with garbage collection