NumPy和Matplotlib的垃圾回收机制

8

我有一个Python脚本,用于不同参数(QK)进行许多模拟、绘制结果并将其存储到磁盘。

每个参数集(Q,K)会产生一个200x200x80数据点的3D体积网格,需要约100 MB的数据。其中一部分体积网格将逐层绘制,产生大约60张图像。

问题是Python在这个过程中显然没有释放内存。我不确定内存泄漏出现在哪里,或者规定Python决定哪些对象被解除分配。我也不确定内存是在numpy数组还是在matplotlib图形对象中丢失的。

  1. 有简单的方法分析Python中哪些对象保留在内存中,哪些自动解除分配?
  2. 有办法强制Python解除分配所有在特定循环周期或特定函数调用中创建的数组和图形对象吗?

相关代码部分在此(但它无法运行...因为包括ctypes C++/Python接口的大部分模拟代码都被省略了,因为太复杂了):

import numpy as np
import matplotlib.pyplot as plt
import ProbeParticle as PP # this is my C++/Python simulation library, take it as blackbox

def relaxedScan3D( xTips, yTips, zTips ):
    ntips = len(zTips); 
    print " zTips : ",zTips
    rTips = np.zeros((ntips,3)) # is this array deallocated when exiting the function?
    rs    = np.zeros((ntips,3)) # and this?
    fs    = np.zeros((ntips,3)) # and this?
    rTips[:,0] = 1.0
    rTips[:,1] = 1.0
    rTips[:,2] = zTips 
    fzs    = np.zeros(( len(zTips), len(yTips ), len(xTips ) )); # and this?
    for ix,x in enumerate( xTips  ):
        print "relax ix:", ix
        rTips[:,0] = x
        for iy,y in enumerate( yTips  ):
            rTips[:,1] = y
            itrav = PP.relaxTipStroke( rTips, rs, fs ) / float( len(zTips) )
            fzs[:,iy,ix] = fs[:,2].copy()
    return fzs


def plotImages( prefix, F, slices ):
    for ii,i in enumerate(slices):
        print " plotting ", i
        plt.figure( figsize=( 10,10 ) ) # Is this figure deallocated when exiting the function ?
        plt.imshow( F[i], origin='image', interpolation=PP.params['imageInterpolation'], cmap=PP.params['colorscale'], extent=extent )
        z = zTips[i] - PP.params['moleculeShift' ][2]
        plt.colorbar();
        plt.xlabel(r' Tip_x $\AA$')
        plt.ylabel(r' Tip_y $\AA$')
        plt.title( r"Tip_z = %2.2f $\AA$" %z  )
        plt.savefig( prefix+'_%3.3i.png' %i, bbox_inches='tight' )

Ks = [ 0.125, 0.25, 0.5, 1.0 ]
Qs = [ -0.4, -0.3, -0.2, -0.1, 0.0, +0.1, +0.2, +0.3, +0.4 ]

for iq,Q in enumerate( Qs ):
    FF = FFLJ + FFel * Q
    PP.setFF_Pointer( FF )
    for ik,K in enumerate( Ks ):
        dirname = "Q%1.2fK%1.2f" %(Q,K)
        os.makedirs( dirname )
        PP.setTip( kSpring = np.array((K,K,0.0))/-PP.eVA_Nm )
        fzs = relaxedScan3D( xTips, yTips, zTips ) # is memory of "fzs" recycled or does it consume more memory each cycle of the loop ?
        PP.saveXSF( dirname+'/OutFz.xsf', headScan, lvecScan, fzs )
        dfs = PP.Fz2df( fzs, dz = dz, k0 = PP.params['kCantilever'], f0=PP.params['f0Cantilever'], n=int(PP.params['Amplitude']/dz) ) # is memory of "dfs" recycled?
        plotImages( dirname+"/df", dfs, slices = range( 0, len(dfs) ) )

3
问题在于你一直保持着所有的图形一直处于打开状态。如果你想要使用 pyplot 的状态机接口,你需要每次显式地关闭图形。否则这些图形将一直被保留下来,以便在调用 plt.show 时进行显示。为了快速解决问题,在 plt.savefig 后调用 plt.close() - Joe Kington
Ahoj Prokope,尝试一下另一种利用matplotlib的方式 >>> [Interactive Applications Using Matplotlib; Benjamin V. Root, (2015)] - user3666197
作为开胃菜,您可能会喜欢查看一个嵌入式MVC-live-matplotlib-GUI示例 >>> https://dev59.com/J4Pba4cB1Zd3GeqPp0Sh#25769600 - user3666197
关于内存管理,了解一下Python可用的内存分析工具。顺便说一句,Python释放内存时更加犹豫,更不愿意将其返回给操作系统。在高性能计算场景中,分布式进程可以让您避免在主线程/进程中遭受这种情况。然而,大多数指出的问题都可以通过使用实时GUI或使用愚蠢的强制.clf() / .close()方法来解决(预防)。 - user3666197
1个回答

11

尝试重复利用你的数字:

plt.figure(0, figsize=(10, 10))
plt.clf() #clears figure

保存后关闭窗口:

...
plt.savefig(...)
plt.close()

2
啊哈,谢谢,使用 plt.close() 后似乎不再有内存泄漏了。但是最好能够清楚地了解规则并分析这些泄漏。 - Prokop Hapala
如果您使用pyplot plt.figure,就不会有泄漏问题。每次调用plt.figure都会给您一个新的图形,旧的图形仍然可以通过调用“plt.figure(n)”来使用,其中n是图形的编号。 - tillsten

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