7

I'm running a python program which replots a graph every few minutes, but every time it runs the memory it uses goes up a little bit, and soon the raspberry pi I'm using slows to a crawl.

Here's the relevant piece of code:

import matplotlib.pyplot as plt
import matplotlib.dates as md
from memory_profiler import profile

@profile
def plotter(file_name, plot_name):
    with open(filen_name, 'r') as readings:
        reader = csv.reader(readings, delimiter=',')
        data = [row for row in reader]

    plt.plot(data[2], data[0])
    ax = gca()
    xfmt = md.DateFormatter('%H:%M') # xaxis is datetimes
    ax.xaxis.set_major_formatter(xfmt)
    plt.legend()

    plt.savefig(plot_name, transparent=True)
    plt.clf()
    plt.cla()
    plt.close()

And the function is called with something like:

 while True:
     plotter(file_name, plot_name)
     sleep(100)

The memory_profiler spits out nice output, but it always looks like this:

Line #    Mem usage    Increment   Line Contents
================================================
38     36.2 MiB      0.6 MiB       plt.savefig(plot_name, transparent=True)
39     36.2 MiB      0.0 MiB       plt.clf()
40     36.2 MiB      0.0 MiB       plt.cla()
41     36.2 MiB      0.0 MiB       plt.close()

(The rest of the function doesn't increase memory usage.)
The memory is incrementing up at savefig(), but is never released despite the various ways I've tried to close the figure. Anyone know why the close() doesn't release the memory?


Edit:
I've tried another method.

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot( some arguments)
fig.savefig(filename)

plt.close('all')
del ax, fig

This doesn't decrement the memory usage either.


Edit 2:
From this answer elsewhere (Create a figure that is reference counted) I've tried the following:

from matplotlib.backends.backend_svg import FigureCanvas
from matplotlib.figure import Figure

fig = Figure()
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111)
ax.plot etc...
fig.savefig(file_name)
plt.close('all')

This also doesn't seem to work. Memory still increasing monotonically and always at the savefig line of the function.

thosphor
  • 2,493
  • 7
  • 26
  • 42
  • 2
    maybe force the call to garbage collector? also try `plt = None` – Jean-François Fabre Oct 25 '17 at 13:12
  • "it always looks like this" So mem usage is always 36.2 MiB? That does not look like a problem. – Stop harming Monica Oct 25 '17 at 13:50
  • @Goyo Sorry, what I meant was the pattern always looks like this. I.e., all lines increment=0 except the `savefig` line which increments by some value. – thosphor Oct 25 '17 at 13:55
  • @user3087409 I tried to replicate the issue and gave up after fixing the fourth error I got running your code. Maybe make it a [mcve] instead of just relevant. – Stop harming Monica Oct 25 '17 at 14:04
  • @user3087409 The backend might be important too. – Stop harming Monica Oct 25 '17 at 14:21
  • @Goyo I'm using the SVG backend – thosphor Oct 25 '17 at 14:22
  • I wonder why the first edit method does not work. The second edit method will not work since `pyplot` is not aware of the figure you created outside of pyplot using FigureCanvas. In this case, `del fig, canvas` could help. In general, are you sure, that the garbage collector is not collecting the deleted elements? – ImportanceOfBeingErnest Oct 25 '17 at 15:09
  • @Jean-FrançoisFabre if (semantically speaking) you want to get rid of the name `plt`, use `del plt`. If you actually want it to point to `None`, use `plt = None`. But you probably meant the former. Both should reduce the reference count of whatever `plt` was referring to, but `del` is what signals the actual intent here. – Andras Deak -- Слава Україні Oct 25 '17 at 23:17

2 Answers2

7

I encountered the same issue. After looking around I found a solution: you need to close the fig AND all windows.

import matplotlib.pylab as plt

fig,ax = plt.subplots(1)
plt.plot(X, Y)
fig.savefig(img_name)
fig.clf()
plt.close()

After closing both the plot and figure, my memory stayed at a more or less constant level.

You can also close the axis using:

ax.cla()
Marc-V
  • 71
  • 1
  • 6
0
        # Clear the current axes.
        plt.cla() 
        # Clear the current figure.
        plt.clf() 
        # Closes all the figure windows.
        plt.close('all')   
        plt.close(fig)
        gc.collect()

Try adding these lines at the end of the loop! It worked for me. (for sub-plot)

Sadman Sakib
  • 557
  • 3
  • 10