43

I have a data analysis module that contains functions which call on the matplotlib.pyplot API multiple times to generate up to 30 figures in each run. These figures get immediately written to disk after they are generated, and so I need to clear them from memory.

Currently, at the end of each of my function, I do:

import matplotlib.pyplot as plt

plt.clf()

However, I'm not too sure if this statement can actually clear the memory. I'm especially concerned since I see that each time I run my module for debugging, my free memory space keeps decreasing.

What do I need to do to really clear my memory each time after I have written the plots to disk?

tdy
  • 36,675
  • 19
  • 86
  • 83
AKKO
  • 973
  • 2
  • 10
  • 18
  • 13
    Have you tried `plt.close("all")`? I have a script that creates about 60 charts with 3 axes each. I use this at the end of each iteration. Memory consumption seems normal enough. – WGS Feb 27 '15 at 04:14
  • 2
    Thanks for your answer! It worked! And I also found this great stackoverflow post http://stackoverflow.com/questions/8213522/matplotlib-clearing-a-plot-when-to-use-cla-clf-or-close that has the answer to my question! – AKKO Mar 02 '15 at 02:32
  • The post http://stackoverflow.com/questions/8213522/matplotlib-clearing-a-plot-when-to-use-cla-clf-or-close does not include plt.close("all"). I tried using all the suggested options in that post, but only plt.close("all") solved a problem I had myself. – vestland May 02 '17 at 12:21
  • Possible duplicate of [When to use cla(), clf() or close() for clearing a plot in matplotlib?](https://stackoverflow.com/questions/8213522/when-to-use-cla-clf-or-close-for-clearing-a-plot-in-matplotlib) – sbodd Apr 08 '19 at 06:42
  • Does this answer your question? [How can I release memory after creating matplotlib figures](https://stackoverflow.com/questions/7101404/how-can-i-release-memory-after-creating-matplotlib-figures) – losnihciL Nov 30 '20 at 11:39
  • This one uses multiproc to avoid the problem: [matplotlib-errors-result-in-a-memory-leak-how-can-i-free-up-that-memory](https://stackoverflow.com/questions/7125710/matplotlib-errors-result-in-a-memory-leak-how-can-i-free-up-that-memory) – D A Dec 09 '21 at 18:26

6 Answers6

34

After one week trials, I got my solution! Hope it can help you. My demo is attached.

import matplotlib.pyplot as plt
import numpy as np

A = np.arange(1,5)
B = A**2

cnt=0
while(1):  
    cnt = cnt+1
    print("########### test %d ###########" % cnt)

    # here is the trick: 
    # set the figure a 'num' to prevent from re-malloc of a figure in the next loop 
    # and set "clear=True" to make the figure clear
    # I never use plt.close() to kill the figure, because I found it doesn't work.
    # Only one figure is allocated, which can be self-released when the program quits.
    # Before: 6000 times calling of plt.figure() ~ about 1.6GB of memory leak
    # Now: the memory keeps in a stable level
    fig = plt.figure(num=1, clear=True)
    ax = fig.add_subplot()

    # alternatively use an other function in one line
    # fig, ax = plt.subplots(num=1,clear=True)

    ax.plot(A,B)
    ax.plot(B,A)

    # Here add the functions you need 
    # plt.show()
    fig.savefig('%d.png' % cnt)
James Huang
  • 441
  • 4
  • 2
  • 4
    Can testify, works like a charm. Beware, if you use plt.close(f) in combination with the above solution, the memory leak still occurs. – Guillermo J. Nov 29 '21 at 12:36
  • Perfect solution! In my end, I added ``fig.clear()`` and ``plt.close(fig)`` after saving figures to completely clean the RAM. – jared Apr 09 '22 at 16:25
  • THANK YOU. Agreed that using plt.close() is the WRONG way to go. THANK YOU! – Ed Landau Jul 08 '22 at 19:34
  • Completely solved my issue after many confusing attempts - amazing! – SteveZissou Sep 13 '22 at 16:37
  • num=1, clear=True worked perfectly. You are a life saver. I had scripts creating dozens of charts for hundreds of geographies, and it was ballooning to 100gb in memory. fig.clear and plt.close were not working for me, but after this the script runs along at only 1.5 gigs of ram, which is essentially just the actual dataset held in memory. – matthewiannowlin Aug 07 '23 at 14:31
29

Especially when you are running multiple processes or threads, it is much better to define your figure variable and work with it directly:

from matplotlib import pyplot as plt

f = plt.figure()
f.clear()
plt.close(f)

In any case, you must combine the use of plt.clear() and plt.close()

UPDATE (2021/01/21)

If you are using a MacOS system along with its default backend (referred as 'MacOSX'), this does NOT work (at least in Big Sur). The only solution I have found is to switch to other of the well-known backends, such as TkAgg, Cairo, etc. To do it, just type:

import matplotlib
matplotlib.use('TkAgg') # Your favorite interactive or non-interactive backend
Luis DG
  • 498
  • 5
  • 8
  • i was able get around this problem with a mac by forcing the garbage collector with the `gc` module: `gc.collect(generation=2)` after closing, clearing, and flagging with `del` – probinso Apr 24 '23 at 19:30
6

I have data analysis module that contains functions which call on Matplotlib pyplot API multiple

Can you edit your functions which is calling matplotlib? I was facing the same issue, I tried following command but none of it worked.

plt.close(fig)
fig.clf()
gc.collect()
%reset_selective -f fig

Then one trick worked for me, instead of creating a new figure every time, I pass the same fig object to the function and this solved my issue.

for example use,

fig = plt.figure()
for i in range(100):
    plt.plot(x,y)

instead of,

for i in range(100):
    fig = plt.figure()
    plt.plot(x,y)
Pritesh Gohil
  • 416
  • 7
  • 15
5

This is more of a test suite than an answer to the question. Here I show that as of Dec 2021, no provided solution actually clears the memory.

We need the following libraries:

import os
import psutil 
import numpy
import matplotlib
import matplotlib.pyplot

I create a single function which should clear ALL matplotlib memory:

def MatplotlibClearMemory():
    #usedbackend = matplotlib.get_backend()
    #matplotlib.use('Cairo')
    allfignums = matplotlib.pyplot.get_fignums()
    for i in allfignums:
        fig = matplotlib.pyplot.figure(i)
        fig.clear()
        matplotlib.pyplot.close( fig )
    #matplotlib.use(usedbackend) 

I make a script that creates 100 figures then attempts to delete them all from memory:

#Use TkAgg backend because it works better for some reason:
matplotlib.use('TkAgg')

#Create fake data for our figures:
x = numpy.arange(1000)

#Get system process information for printing memory usage:
process = psutil.Process(os.getpid())

#Check memory usage before we create figures:
print('BeforeFigures: ', process.memory_info().rss)  # in bytes

#Make 100 figures, and check memory usage:
for n in range(100):
    matplotlib.pyplot.figure()
    matplotlib.pyplot.plot(x, x)
print('AfterFigures:  ', process.memory_info().rss)  # in bytes

#Clear the all the figures and check memory usage:
MatplotlibClearMemory( )
print('AfterDeletion: ', process.memory_info().rss)  # in bytes

Which outputs memory remaining:

>>> BeforeFigures: 76083200
>>> AfterFigures:  556888064
>>> AfterDeletion: 335499264

Less than half the memory allocated is cleared (much less if using standard back-end). The only working solutions on this stack overflow page avoid placing multiple figures in memory simultaneously.

D A
  • 3,130
  • 4
  • 25
  • 41
  • This seems to asymptotically approach clearing half the memory as I create more figures, or make them hold more data. – D A Sep 18 '21 at 01:36
1

There is unfortunaltely no solution:

See: https://github.com/matplotlib/matplotlib/issues/20300

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.

Matze
  • 46
  • 3
  • This should be the accepted answer! Not only is there no solution, there is agreement in the matplotlib development community to never fix the problem. – D A Apr 24 '23 at 13:36
  • "[Issue closed] as 'won't fix' because the memory leak doesn't happen for proper usage" – D A Apr 24 '23 at 13:38
-1

Late answer, but this worked for me. I had a long sequential code generating many plots, and it would always end up eating all the RAM by the end of the process. Rather than calling fig.close() after each figure is complete, I have simply redefined the plt.figure function as follows, so that it is done automatically:

import matplotlib.pyplot as plt
import copy
try:
   # if script it run multiple times, only redefine once
   plt.old_figure
except:
  # matplotlib is imported for the first time --> redefine
  plt.old_figure = copy.deepcopy(plt.figure)
  def newfig(*args):
    plt.show()
    plt.close("all")
  return plt.old_figure(*args)
  plt.figure = newfig

I am well aware that this is not a nice solution, however it easy, quick, and did the trick for me ! Maybe there's a way to decorate plt.figure instead of redefining it.

Laurent90
  • 284
  • 1
  • 10