Memory usage of AverageTFR.plot()

I’m computing and plotting several TFRs in a loop using AverageTFR.plot(). If I do not explicitly pass (a list of) matplotlib.Axes objects (i.e. the axes parameter gets its default value of None), the following RuntimeWarning is issued:

More than 20 figures have been opened. Figures created through the pyplot interface (matplotlib.pyplot.figure) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam figure.max_open_warning).

Apparently, this warning is not generated by any MNE function, but I’m still asking this question here because it occurs when using AverageTFR.plot(). Indeed, I then tried to roll my own figure and axes objects (passing the latter to the axes parameter) and making sure to close the figure before plotting the next one. However, this doesn’t reduce memory usage at all, it doesn’t seem to make any difference.

Here’s a minimal example to demonstrate what I mean:

import numpy as np
import mne
import matplotlib.pyplot as plt


n_channels = 64
n_freqs = 200
sfreq = 512
n_times = sfreq
nave = 60

rng = np.random.default_rng(seed=1)

for i in range(10):
    fig, axes = plt.subplots(1, n_channels)
    info = mne.create_info(n_channels, sfreq)
    data = rng.standard_normal((n_channels, n_freqs, n_times))
    times = np.arange(0, n_times / sfreq, 1 / sfreq)
    freqs = np.linspace(0, 50, n_freqs)
    tfr = mne.time_frequency.AverageTFR(info, data, times, freqs, nave)
    tfr.plot(picks="all", axes=axes, show=False)
    plt.close(fig)

I saved this script as memtest.py and ran it with the mprof function from the memory-profiler package:

mprof run -T 0.05 memtest.py

The output obtained with mprof plot looks like this:

Apparently, closing the figure does not free any memory. For real data, this completely fills my 16GB of RAM and basically makes it impossible to loop over TFRs and plot them (actually I do not even want to plot, but I’m using fig.save() to export the figure to PNG).

Is this expected behavior or maybe a bug (if so, is it MNE or Matplotlib)? Maybe I’m not handling this correctly, so I’d appreciate any advice on how to efficiently plot multiple TFRs without swamping my RAM.

Can you try re-using the figure (that is, create it outside of the for loop)?

Have you tried calling the garbage collector manually after closing the figure?

Moving that line out of the loop reduces memory usage a little (it maxes out at around 1630MB as compared to 2240MB), but doesn’t change the fact that it is constantly growing.

This doesn’t seem to change anything.