使用Matplotlib时出现内存泄漏问题

6

这并不是一个bug报告 - 即使这些泄漏可能是mpl的bug导致的,请理解问题是要求绕过它们的方式。

问题很简单:绘制大量数据(使用plot()或scatter()),清除/释放所有内容,垃圾回收,但仍然没有释放近乎所有的内存。

Line #    Mem usage    Increment   Line Contents
================================================
391  122.312 MiB    0.000 MiB   @profile
392                             def plot_network_scatterplot(t_sim_stop, spikes_mat, n_cells_per_area, n_cells, basedir_output, condition_idx):
393
394                                  # make network scatterplot
395  122.312 MiB    0.000 MiB        w, h = plt.figaspect(.1/(t_sim_stop/1E3))
396  122.324 MiB    0.012 MiB        fig = mpl.figure.Figure(figsize=(10*w, 10*h))
397  122.328 MiB    0.004 MiB        canvas = FigureCanvas(fig)
398  122.879 MiB    0.551 MiB        ax = fig.add_axes([.01, .1, .98, .8])
399  134.879 MiB   12.000 MiB        edgecolor_vec = np.array([(1., 0., 0.), (0., 0., 1.)])[1-((spikes_mat[:,3]+1)/2).astype(np.int)]
400                                  '''pathcoll = ax.scatter(spikes_mat[:,1],
401                                             spikes_mat[:,0] + n_cells_per_area * (spikes_mat[:,2]-1),
402                                             s=.5,
403                                             c=spikes_mat[:,3],
404                                             edgecolor=edgecolor_vec)'''
405  440.098 MiB  305.219 MiB        pathcoll = ax.plot(np.random.rand(10000000), np.random.rand(10000000))
406  440.098 MiB    0.000 MiB        ax.set_xlim([0., t_sim_stop])
407  440.098 MiB    0.000 MiB        ax.set_ylim([1, n_cells])
408  440.098 MiB    0.000 MiB        plt.xlabel('Time [ms]')
409  440.098 MiB    0.000 MiB        plt.ylabel('Cell ID')
410  440.098 MiB    0.000 MiB        plt.suptitle('Network activity scatterplot')
411                                  #plt.savefig(os.path.join(basedir_output, 'network_scatterplot-[cond=' + str(condition_idx) + '].png'))
412  931.898 MiB  491.801 MiB        canvas.print_figure(os.path.join(basedir_output, 'network_scatterplot-[cond=' + str(condition_idx) + '].png'))
413                                  #fig.canvas.close()
414                                  #pathcoll.set_offsets([])
415                                  #pathcoll.remove()
416  931.898 MiB    0.000 MiB        ax.cla()
417  931.898 MiB    0.000 MiB        ax.clear()
418  931.898 MiB    0.000 MiB        fig.clf()
419  931.898 MiB    0.000 MiB        fig.clear()
420  931.898 MiB    0.000 MiB        plt.clf()
421  932.352 MiB    0.453 MiB        plt.cla()
422  932.352 MiB    0.000 MiB        plt.close(fig)
423  932.352 MiB    0.000 MiB        plt.close()
424  932.352 MiB    0.000 MiB        del fig
425  932.352 MiB    0.000 MiB        del ax
426  932.352 MiB    0.000 MiB        del pathcoll
427  932.352 MiB    0.000 MiB        del edgecolor_vec
428  932.352 MiB    0.000 MiB        del canvas
429  505.094 MiB -427.258 MiB        gc.collect()
430  505.094 MiB    0.000 MiB        plt.close('all')
431  505.094 MiB    0.000 MiB        gc.collect()

我已经尝试了许多不同的清除和释放内存的组合和顺序,但都没有成功。我尝试过使用mpl.pyplot而非显式的fig/canvas创建方式,但结果相同。
是否有任何方法可以释放这些内存并返回原来的122.312?
谢谢!
1个回答

7

Alex Martelli解释

通常情况下,一个进程很难将内存“归还给操作系统”(除非该进程终止并且操作系统重新获取所有内存),因为(在大多数实现中)malloc返回的是从大块中划分出来的内存,以提高效率,但是如果仍然有一部分正在使用,则无法返回整个块。因此,你认为的内存泄漏可能只是这种情况的副作用。如果确实如此,那么使用fork可以解决这个问题。

此外

确保在完成对内存的大量但是临时的使用后,所有资源都会返回到系统中的唯一可靠方法是,在子进程中进行此类使用,该子进程执行内存密集型任务然后终止。

因此,你可以使用multiprocessing在单独的进程中运行plot_network_scatterplot,而不是尝试清除图形和轴,删除引用和垃圾收集(所有这些方法都无法解决问题):

import multiprocessing as mp

def plot_network_scatterplot(
    t_sim_stop, spikes_mat, n_cells_per_area, n_cells, basedir_output, 
    condition_idx):

    # make network scatterplot
    w, h = plt.figaspect(.1/(t_sim_stop/1E3))
    fig = mpl.figure.Figure(figsize=(10*w, 10*h))
    canvas = FigureCanvas(fig)
    ax = fig.add_axes([.01, .1, .98, .8])
    edgecolor_vec = np.array([(1., 0., 0.), (0., 0., 1.)])[1-((spikes_mat[:,3]+1)/2).astype(np.int)]
    '''pathcoll = ax.scatter(spikes_mat[:,1],
               spikes_mat[:,0] + n_cells_per_area * (spikes_mat[:,2]-1),
               s=.5,
               c=spikes_mat[:,3],
               edgecolor=edgecolor_vec)'''
    pathcoll = ax.plot(np.random.rand(10000000), np.random.rand(10000000))
    ax.set_xlim([0., t_sim_stop])
    ax.set_ylim([1, n_cells])
    plt.xlabel('Time [ms]')
    plt.ylabel('Cell ID')
    plt.suptitle('Network activity scatterplot')
    canvas.print_figure(os.path.join(basedir_output, 'network_scatterplot-[cond=' + str(condition_idx) + '].png'))

def spawn(func, *args):
    proc = mp.Process(target=func, args=args)
    proc.start()
    # wait until proc terminates.
    proc.join()

if __name__ == '__main__':
    spawn(plot_network_scatterplot, t_sim_stop, spikes_mat, n_cells_per_area, 
          n_cells, basedir_output, condition_idx)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接