Python struct.pack causing a memory leak, even after deleting object
我正在使用pyaudio检测音频源的活动。我正在一个经常死掉的流中创建每个音频事件的wav文件。使用memory_profiler,我注意到record_to_file()方法中的pack方法通常使用的内存量是删除数据对象所能恢复的4倍。此代码取自检测和录制python中的音频
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | from sys import byteorder import sys from array import array import struct import gc import pyaudio import wave import subprocess import objgraph from memory_profiler import profile from guppy import hpy THRESHOLD = 5000 CHUNK_SIZE = 1024 FORMAT = pyaudio.paInt16 RATE = 44100 def is_silent(snd_data): "Returns 'True' if below the 'silent' threshold" return max(snd_data) < THRESHOLD def normalize(snd_data): "Average the volume out" MAXIMUM = 16384 times = float(MAXIMUM)/max(abs(i) for i in snd_data) r = array('h') for i in snd_data: r.append(int(i*times)) return r def trim(snd_data): "Trim the blank spots at the start and end" def _trim(snd_data): snd_started = False r = array('h') for i in snd_data: if not snd_started and abs(i)>THRESHOLD: snd_started = True r.append(i) elif snd_started: r.append(i) return r # Trim to the left snd_data = _trim(snd_data) # Trim to the right snd_data.reverse() snd_data = _trim(snd_data) snd_data.reverse() return snd_data def add_silence(snd_data, seconds): "Add silence to the start and end of 'snd_data' of length 'seconds' (float)" r = array('h', [0 for i in xrange(int(seconds*RATE))]) r.extend(snd_data) r.extend([0 for i in xrange(int(seconds*RATE))]) return r def record(): """g Record a word or words from the microphone and return the data as an array of signed shorts. Normalizes the audio, trims silence from the start and end, and pads with 0.5 seconds of blank sound to make sure VLC et al can play it without getting chopped off. """ p = pyaudio.PyAudio() stream = p.open(format=FORMAT, channels=1, rate=RATE, input=True, output=True, frames_per_buffer=CHUNK_SIZE) num_silent = 0 snd_started = False r = array('h') while 1: # little endian, signed short snd_data = array('h', stream.read(CHUNK_SIZE)) if byteorder == 'big': snd_data.byteswap() r.extend(snd_data) silent = is_silent(snd_data) if silent and snd_started: num_silent += 1 elif not silent and not snd_started: snd_started = True if snd_started and num_silent > 400: break sample_width = p.get_sample_size(FORMAT) stream.stop_stream() stream.close() p.terminate() #r = normalize(r) r = trim(r) r = add_silence(r, 0.5) return sample_width, r @profile def record_to_file(path): "Records from the microphone and outputs the resulting data to 'path'" sample_width, data = record() data = struct.pack('<' + ('h'*len(data)), *data) print(sys.getsizeof(data)) wf = wave.open(path, 'wb') wf.setnchannels(1) wf.setsampwidth(sample_width) wf.setframerate(RATE) wf.writeframes(data) wf.close() del data gc.collect() if __name__ == '__main__': count = 1 h = hpy() f = open('heap.txt','w') objgraph.show_growth(limit=3) while(1): filename = 'demo' + str(count) + '.wav' print("please speak a word into the microphone") record_to_file(filename) print("done - result written to {0}".format(filename)) cmd = 'cd"C:\\Users\\user\\Desktop\ audio" & ffmpeg\\bin\\ffmpeg -i {0} -acodec libmp3lame {1}.mp3'.format(filename, filename) "subprocess.call(cmd, shell=True)" count += 1 objgraph.show_growth() print h.heap() |
下面是内存分析器模块输出的一个迭代:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Line # Mem usage Increment Line Contents ================================================ 117 19.6 MiB 0.0 MiB @profile 118 def record_to_file(path): 119 "Records from the microphone and outputs the resulting data to 'path'" 120 22.4 MiB 2.8 MiB sample_width, data = record() 125 31.3 MiB 8.9 MiB data = struct.pack('<' + ('h'*len(data)), *data) 126 31.3 MiB 0.0 MiB print(sys.getsizeof(data)) 127 31.3 MiB 0.0 MiB wf = wave.open(path, 'wb') 128 31.3 MiB 0.0 MiB wf.setnchannels(1) 129 31.3 MiB 0.0 MiB wf.setsampwidth(sample_width) 130 31.4 MiB 0.0 MiB wf.setframerate(RATE) 131 31.4 MiB 0.0 MiB wf.writeframes(data) 132 31.4 MiB 0.0 MiB wf.close() 133 30.4 MiB -0.9 MiB del data 134 27.9 MiB -2.5 MiB gc.collect() |
号
使用vs调试进程时,我看到系统内存中有大量"h"字符,这可能是发生泄漏的原因。任何帮助都将不胜感激
struct类保留项目缓存,以便更快地访问。清除结构缓存的唯一方法是调用
警告!这是一种
这里和这里的python论坛上都有关于这个"内存泄漏"的讨论。