需要帮助识别matplotlib和flask的内存泄漏问题
我用flask框架写了一个小的网页应用,这个应用需要用matplotlib来绘图。问题是每次我创建图表时,程序消耗的内存越来越多。
我通过mod_wsgi部署了这个应用,.wsgi文件看起来很简单,如下所示:
from yourapplication import app as application
问题出现在我访问生成图表的那个网址时。这个函数会创建一个绘图对象,当它被初始化时,会从sqlite3数据库中获取相关数据(这些数据大约有30个整数和同样多的日期时间对象),然后用matplotlib生成一个图表,并返回一个StringIO对象,最后在屏幕上显示出来。
这就是这个函数的全部内容。整个类的代码可以在这里查看。
canvas = FigureCanvas(fig)
png_output = StringIO.StringIO()
canvas.print_png(png_output)
return png_output.getvalue()
当我访问这个网站时,会创建一个大约占用25MB内存的进程。第一次创建图表时,内存使用量增加到30MB,然后每次使用绘图类时大约增加1MB。默认设置是使用5个进程,这样消耗的内存太多了(几分钟内就达到了150MB,而我只能使用80MB)。
我对这些技术(网页框架、apache、数据库)都很陌生,所以我不知道问题可能出在哪里,任何建议都非常感谢!
3 个回答
我在用Flask生成一系列图表的时候,也遇到了和你一样的内存泄漏问题。在matplotlib的文档中,有一部分提到在网页应用中使用matplotlib时,建议不要用matplotlib.pyplot
,而是用matplotlib.figure.Figure
来避免内存泄漏。需要注意的是,你的Matplotlib版本要在3.1或以上。
这取决于你是如何构建图表的(命令行接口还是面向对象接口)。把Pyplot类换成Figure类其实很简单。
import matplotlib.pyplot as plt
fig = plt.figure()
然后只需要把那些在命令行API中不工作的代码替换成面向对象API的代码就可以了。
from matplotlib.figure import Figure
fig = Figure()
发这个是希望能帮助到未来的某个人。
我遇到了同样的问题,觉得axel22给出的答案对我没有用。
经过一番折腾,我发现有两个问题:
- 我没有清理Matplotlib的图形,导致它一直占用内存
- 我在代码的错误位置调用了垃圾回收器
第一个问题
我之前是这样做的(错误):
fig = util.create_figure(....)
output = io.BytesIO()
canvas = FigureCanvas(fig)
canvas.print_png(output)
但我应该这样做(正确):
fig = util.create_figure(....)
output = io.BytesIO()
canvas = FigureCanvas(fig)
canvas.print_png(output)
# Clears the figure from memory
fig.clf()
第二个问题
我在代码的错误位置调用了垃圾回收器。你需要在FigureCanvas调用的范围之外去调用它。
这样做是没有用的(错误):
import gc
def do_something():
canvas = FigureCanvas(fig)
png_output = StringIO.StringIO()
canvas.print_png(png_output)
gc.collect()
return png_output.getvalue()
do_something()
但这样做就有效了(正确):
import gc
def do_something():
canvas = FigureCanvas(fig)
png_output = StringIO.StringIO()
canvas.print_png(png_output)
return png_output.getvalue()
do_something()
gc.collect()
在每次调用plot_month
函数之后这样做,就解决了内存泄漏的问题。
import gc
gc.collect()