Python 内存泄漏调查

43 阅读4分钟

我编写了一些代码来处理一组文件,将它们拼接在一起并绘制成图。代码的大致流程如下:

  1. 遍历指定目录下的所有文件和子目录。
  2. 在每个子目录中,查找并提取符合特定命名规则的数据文件。
  3. 从每个数据文件中读取数据并进行一些处理,包括数据拆分、噪声消除等。
  4. 将处理后的数据绘制成频谱图、时域图和频域图。
  5. 将绘制的图像保存到指定路径。

代码在运行一段时间后会占用大量的内存,最终导致程序崩溃。经过调查,发现问题出在数据处理和绘图的过程中,其中可能存在内存泄漏。

2、解决方案

2.1 减少不必要的变量

在代码中,我发现了一些不必要的变量被创建并在内存中保存,即使它们不再被使用。例如,在绘制频谱图时,我使用了一个名为 spec1 的变量来存储频谱图数据,但在绘图结束后,我并没有释放这个变量,导致它一直驻留在内存中。

2.2 及时释放内存

在使用完变量或对象后,我应该及时释放它们占用的内存。例如,在读取完一个数据文件后,我可以使用 del 语句来删除不再需要的变量,以便操作系统回收这些内存。

2.3 使用缓存技术

在某些情况下,如果需要对大量数据进行重复处理,可以使用缓存技术来减少内存的使用。例如,在绘制时域图时,我需要对数据进行傅里叶变换。我可以将傅里叶变换的结果缓存起来,这样在绘制其他时域图时,就可以直接从缓存中获取结果,而无需重新计算。

2.4 使用内存分析工具

可以使用内存分析工具来帮助查找内存泄漏的问题。例如,在 Python 中,可以使用 memory_profiler 库来分析内存的使用情况,并找出哪些变量或对象占用了过多的内存。

代码示例

以下是我对部分代码进行修改后的示例:

import gc

for paths, dirs, files in os.walk(start_path):
    for d in dirs:
        path = start_path + changedir + d
        pathpart = d

        os.chdir(path)
        for file in glob.glob("*-0.dat"):
            tempname = file.split("-")
            fileName1 = str(tempname[0] + "-" + tempname[1]+ "-")
            gc.collect()

            Chan2 = []
            Chan1 = []
            temp_1 = []
            temp_2 = []
            temp_3 = []
            Data_Sets1 = []
            Data_Sets2 = []
            Headers = []


            for fileNumber in range(0,45):
                fileName = fileName1 + str(fileNumber) + fileName3
                header, data1, data2 = u.unpackFile(path,fileName)

                if header == None:
                    logging.warning("curropted file found at " + fileName)
                    Data_Sets1.append(temp_1)
                    Data_Sets2.append(temp_2)
                    Headers.append(temp_3)
                    temp_1 = []
                    temp_2 = []
                    temp_3 = []
                else:
                    logging.info(fileName + " is good!")
                    temp_3.append(header)
                    for i in range(0,10000):
                        temp_1.append(data1[i])
                        temp_2.append(data2[i])

            Data_Sets1.append(temp_1)
            Data_Sets2.append(temp_2)
            Headers.append(temp_3)
            temp_1 = []
            temp_2 = []
            temp_3 = []

            del temp_1
            del temp_2
            del temp_3

            lengths = []
            for i in range(len(Data_Sets1)):
                lengths.append(len(Data_Sets1[i]))
            index = lengths.index(max(lengths))

            Chan1 = Data_Sets1[index]
            Chan2 = Data_Sets2[index]
            Start_Header = Headers[index]
            if (len(Chan1) == 0 | len(Chan2) == 0):
                continue
            try:
                Date = Start_Header[index][0]
                Time = Start_Header[index][1]
            except IndexError:
                logging.critical("file " + fileName + " is unusuable")
                continue
            """
            Clostest_Power = int(np.log(len(Chan1))/np.log(2))
            Length = 2 ** Clostest_Power
            logging.debug("Length of the file is " + str(Length))
            Chan1 = Chan1[0:Length]
            Chan2 = Chan2[0:Length]
            """
            logging.debug("Length of channels is " + str(len(Chan1)))

            window = np.hanning(Window_Width)

            t= s.Time_Array(len(Chan1),Sample_Rate)


            window2 = np.hanning(len(Chan1))

            Noise_Frequincies = []
            for i in range(1,125):
                Noise_Frequincies.append(60.0*float(i))
            Noise_Frequincies.append(180.0)

            filter1 = s.Noise_Reduction(Sample_Rate,Noise_Frequincies,Chan1)
            filter2 = s.Noise_Reduction(Sample_Rate,Noise_Frequincies,Chan2)

            logging.info("Starting the plots")


            fig1, (ax1, ax2) = plt.subplots(nrows=2)


            spec1, freqs1, time1 = mlab.specgram(filter1, NFFT=Window_Width, Fs=Sample_Rate, window=window, noverlap=Over_Lap)


            im1 = ax1.imshow(spec1, cmap=cm.get_cmap("rainbow"), norm=colors.LogNorm(), origin='lower',
                extent=[t[0], t[len(t)-1], freqs1.min(), 8000],aspect='auto',vmin=1e-5,vmax=1e5)

            ax1.set_title(str(Date) + "-" + str(Time) + " Channel 1")
            ax1.set_ylabel("Freqency Hz")



            spec2, freqs2, time2 = mlab.specgram(filter2, NFFT=Window_Width, Fs=Sample_Rate, window=window, noverlap=Over_Lap)

            im2 = ax2.imshow(spec2, cmap=cm.get_cmap("rainbow"), norm=colors.LogNorm(), origin='lower',
                extent=[t[0], t[len(t)-1], freqs2.min(), 8000],aspect='auto',vmin=1e-5,vmax=1e5)

            cax1, kw1 = matplotlib.colorbar.make_axes(ax1)
            colorbar(im1,cax=cax1,**kw1)
            cax2, kw2 = matplotlib.colorbar.make_axes(ax2)
            colorbar(im2,cax=cax2,**kw2)

            ax2.set_title(str(Date) + "-" + str(Time) + " Channel 2")
            ax2.set_ylabel("Freqency Hz")


            save1 = save_path+pathpart + changedir+specgram_path
            if not os.path.exists(save1):
                os.makedirs(save1)
            savefig(os.path.join(save1,str(Date) + "-" + str(Time) + "-Power_Spec1.png"))

            logging.info("Spectrogram path is " + save1)




            fig2, (ax4,ax6) = plt.subplots(nrows=2)
            final_fft = []
            fft = s.Full_FFT(filter1,window2)
            for i in range(0,len(fft)):
                final_fft.append(np.absolute(fft[i]))



            freqs = []
            for i in range(0,len(final_fft)):
                freqs.append(i*Sample_Rate/float(len(final_fft)))

            ax4.plot(freqs, final_fft)

            new_fft = []
            new = s.Full_FFT(filter2,window2)
            for i in range(0,len(new)):
                new_fft.append(np.absolute(new[i]))
            ax6.plot(freqs,new_fft)


            save2 = save_path+pathpart+ changedir + freq_path
            logging.info("Frequency path is " + save2