memory leak with PyQt4 plus savefig or print_figure

sorry, i replied to Mike and not to the list. see below.

···

On Thu, Jul 2, 2009 at 2:57 PM, Ralf Gommers <ralf.gommers@…2670…> wrote:

Thanks for looking into this Mike.

On Thu, Jul 2, 2009 at 10:39 AM, Michael Droettboom <mdroe@…86…> wrote:

It is not surprising that memory usage is much lower without printing the plot. Very little is actually done by the “plot” command other than setting up a tree of objects that is later rendered during the printing process (where most of the work happens).

The attached script based on what you provided doesn’t leak memory for me with matplotlib 0.98.5.2. It would appear that there is something else in your application triggering the leak. Perhaps there is something holding a reference longer than it should?

Your attached script memleak2.py is indeed fine, it never takes up more than 60Mb of RAM. But did you try to run the PyQt4 GUI I attached? There the same save_png() function does increase memory usage for each call.

I’m not sure how to exactly measure memory usage for a GUI program, but it is so large I can look at “System Activity” (or Task Manager on XP) and get the approximate number:

Loading the GUI: 30.5Mb

1st call to save_png: 82Mb
2nd call: 116Mb
10th call: 380Mb

I can see the memory usage drop after some calls, so I guess it is a problem of references being held and sometimes being released as you said. But memory use does seem to be unbounded. Waiting for minutes, or interacting with the GUI, never releases any memory. It is strange, the PyQt4 demo program is fine by itself, save_png() is fine by itself, but combine them and there’s a memory problem.

Cheers,
Ralf

You can see from the attached massif plots that memory usage never becomes unbounded. It is normal for Python to delay deallocation for efficiency reasons. Calling gc.collect (the second graph) does help keep memory usage more compact however, if that is a primary concern.

Cheers,

Mike

Ralf Gommers wrote:

Hi,

I am working on a PyQt4 application with some embedded MPL figures, and am also trying to save some figures as png’s without displaying them. I am observing huge memory increases (10s or 100s of Mb) the moment I try to save a png. I reproduced the issue by combining two mpl examples, http://matplotlib.sourceforge.net/examples/user_interfaces/embedding_in_qt4.html and http://matplotlib.sourceforge.net/examples/api/agg_oo.html. Full code is attached. When pressing the “save figure” button, memory usage shoots up, multiple clicks keep sending it higher (although not monotonically).

I tested on two different platforms

  • Matplotlib 98.5.2 and Python 2.6.2 on Ubuntu.

  • latest Enthought Python Distribution on Windows XP.

The function that does the png saving is:

def save_png():

"""Save an image as a png file"""



pngpath = 'test_mplsave.png'



fig = Figure()

canvas = FigureCanvas(fig)

ax = fig.add_subplot(111)

x = arange(5e3)

ax.plot(x, sin(x))

canvas.print_figure(pngpath)



## tried all things commented out below, all makes no difference ##

#fig.savefig(pngpath)



#del(fig)

#del(canvas)

#del(ax)



#import matplotlib.pyplot as plt

#plt.close(fig)



#import gc

#gc.collect()

Commenting out the canvas.print_figure line fixes the issue.

Am I doing something obviously wrong, or mixing two incompatible ways of doing things?

Cheers,

Ralf





Matplotlib-users mailing list

Matplotlib-users@lists.sourceforge.net

https://lists.sourceforge.net/lists/listinfo/matplotlib-users

Michael Droettboom

Science Software Branch

Operations and Engineering Division

Space Telescope Science Institute

Operated by AURA for NASA

from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas

from matplotlib.figure import Figure

from numpy import arange, sin

import gc

def save_png():

"""Save an image as a png file"""



pngpath = 'test_mplsave.png'



fig = Figure()

canvas = FigureCanvas(fig)

ax = fig.add_subplot(111)

x = arange(5e3)

ax.plot(x, sin(x))

canvas.print_figure(pngpath)

The below is not necessary to prevent a leak, but it does make

# memory usage more compact

gc.collect()

for i in range(100):

save_png()