8000 Memory leak in plt.close() when unshown figures in GUI backends are closed · Issue #20300 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Memory leak in plt.close() when unshown figures in GUI backends are closed #20300

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
P-Takenaka opened this issue May 25, 2021 · 6 comments
Closed

Comments

@P-Takenaka
Copy link

Bug report

Bug summary

When creating multiple plots (e.g. in a loop), the allocated memory for the process increases in each iteration even though the figure is cleared each time.

Code for reproduction

import gc
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(5000):
    fig.clf()
    plt.cla()
    ax.plot([0,1,2,3,4,5], [0,1,2,3,4,5])
    plt.close('all')
    plt.close(fig)

    gc.collect()

Expected outcome

The allocated memory should not increase in each iteration.

Matplotlib version

  • Operating system: Arch Linux
  • Matplotlib version (import matplotlib; print(matplotlib.__version__)): 3.4.2
  • Matplotlib backend (print(matplotlib.get_backend())): TkAgg
  • Python version: 3.9.5
  • Jupyter version (if applicable):
  • Other libraries:

Matplotlib was installed via pip.

@timhoffm
Copy link
Member
timhoffm commented May 25, 2021

This indeed slowly increases memory usage (checked with memory_profiler and 500 samples instead of 5000):

grafik

However, the code is not reasonable in itself. It seems you are trying wildly delete everything. But though that, fig.clf() cleans the figure, including it's axes, plt.cla() does nothing then, and the ax object continues to live detached from any figure so that the data from ax.plot will not show up in any figure.

While that doesn't explain the memory leak, let's try and start from a realistic example: What are you trying to do exactly? Do you only want to replace data inside the Axes? If so, the only cleanup command necessary should be ax.clear() (or historically synonym ax.cla()). If you need to wipe the whole figure, fig.clf() is the way to go (But I expect fig.close(); fig = plt.figure() should be similarly efficient).

@timhoffm timhoffm added the status: needs clarification Issues that need more information to resolve. label May 25, 2021
@P-Takenaka
Copy link
Author
P-Takenaka commented May 25, 2021

I am aware that the example code contains redundant clear commands, I wanted to emphasize that I tried all the commands that solved past issues of a similar kind. However, after your remarks regarding a floating axis object I noticed that this might be partially the reason for the issue, and only the following combination of clear commands in each iteration is already enough to result in the memory growth problem:

plt.cla()
plt.close()

In my use case I only need to clear the axis data, therefore for me the issue would be solved by only using plt.cla() as you said. I unnecessarily went the way to rebuild the whole figure in each iteration at first, resulting in me using the above functions. Though I am still wondering why the code results in the memory leak. Is there no way to completely clean up a figure short of stopping the whole python process? Unfortunately I am not familiar enough with the inner workings of matplotlib.

@timhoffm
Copy link
Member

Regular usage with clearing the Axes or figure does not have a memory leak. It seems that plt.close() doesn't completely get rid of the data.

Axes clearing

fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(500):
    ax.clear()
    ax.plot([0,1,2,3,4,5], [0,1,2,3,4,5])

grafik

Figure clearing

fig = plt.figure()
for i in range(500):
    fig.clf()
    ax = fig.add_subplot(111)
    ax.plot([0,1,2,3,4,5], [0,1,2,3,4,5])

grafik

Figure closing

Simple figure closing

for i in range(500):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot([0,1,2,3,4,5], [0,1,2,3,4,5])
    plt.close(fig)

grafik

Closing with garbage collection

Strips of some memory, but doesn't help with the leak. All this does is that the gc slows down execution:

for i in range(500):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot([0,1,2,3,4,5], [0,1,2,3,4,5])
    plt.close(fig)
    gc.collect()

grafik

@jklymak
Copy link
Member
jklymak commented May 25, 2021

I'm relatively sure tK leaking has been documented before. Does this happen for other backends?

@timhoffm
Copy link
Member
timhoffm commented May 25, 2021

As proposed in #5795, this has to do with created but not-shown GUI windows (all tests done with Qt on linux).

It works if you show the windows and close them afterwards

for i in range(500):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot([0,1,2,3,4,5], [0,1,2,3,4,5])
    plt.show(block=False)
    plt.pause(0.2)
    plt.close(fig)

grafik

The edges are probably due to garbage collection.

It works if you use a non-GUI backend

import matplotlib as mpl
mpl.use('agg')
import matplotlib.pyplot as plt

for i in range(500):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot([0,1,2,3,4,5], [0,1,2,3,4,5])
    plt.savefig(f'test{i:03d}.png')
    plt.close(fig)

grafik

Summary

This only happens if you work in a GUI backend, create new figures, but don't show them. This way of usage is not reasonable. There are working usage patterns for all relevant scenarios.

I'm closing this as "won't fix" because the memory leak doesn't happen for proper usage, and I assume that debugging the GUI framework memory management would be quite hard.

@timhoffm timhoffm changed the title Memory not freed up correctly Memory leak in plt.close() if unshown figures in GUI backends are closed May 25, 2021
@timhoffm timhoffm changed the title Memory leak in plt.close() if unshown figures in GUI backends are closed Memory leak in plt.close() when unshown figures in GUI backends are closed May 25, 2021
@timhoffm timhoffm added status: won't or can't fix and removed status: needs clarification Issues that need more information to resolve. labels May 25, 2021
@tacaswell
Copy link
Member

The edges are probably due to garbage collection.

gc + circular references is the source of the jagged edges, Python only does the more expensive sweeps every so often and we have a lot of circular references!

tnowotny added a commit to CompEphys-team/DroSort that referenced this issue Mar 16, 2022
…ke rate.

Note to self: Also should check how spikerates are estimated as it seems
they have a little bit of a delay of going up after a couple of quick spikes.

Also now found a reference to teh memory leak problem which occurs in gui
backends but not non-gui such as agg.
Allegedly, also does not occur in gui backends if figures are shown and then
closed. This does not seem to be tha case for me
(see matplotlib/matplotlib#20300 for reference)
mkavulich added a commit to dtcenter/mpas_plot that referenced this issue Feb 5, 2025
 - Add parallelism with starmap: script will create plots in parallel
   with the provided number of tasks via the -p argument
 - Change example file to be much smaller
 - Fix problem with memory leakage when making a large number of plots
   Ref: matplotlib/matplotlib#20300
mkavulich added a commit to dtcenter/mpas_plot that referenced this issue Feb 5, 2025
 - Add parallelism with starmap: script will create plots in parallel
   with the provided number of tasks via the -p argument
 - Change example file to be much smaller
 - Fix problem with memory leakage when making a large number of plots
   Ref: matplotlib/matplotlib#20300
mkavulich added a commit to dtcenter/mpas_plot that referenced this issue Feb 5, 2025
 - Add parallelism with starmap: script will create plots in parallel
   with the provided number of tasks via the -p argument
 - Change example file to be much smaller
 - Fix problem with memory leakage when making a large number of plots
   Ref: matplotlib/matplotlib#20300
 - Docstrings for functions
mkavulich added a commit to dtcenter/mpas_plot that referenced this issue Feb 5, 2025
 - Add parallelism with starmap: script will create plots in parallel
   with the provided number of tasks via the -p argument
 - Change example file to be much smaller
 - Fix problem with memory leakage when making a large number of plots
   Ref: matplotlib/matplotlib#20300
 - Docstrings for functions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants
0