python import gc_即使在gc.collect()之后,分配给Python的内存也不会在Linux中释放回来...

本文探讨了一个Python程序中出现的内存泄露问题,特别是在使用numpy和scipy库时。通过实验发现,即使不再使用的内存也无法通过垃圾回收机制释放,该问题在不同操作系统上表现一致。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我已经在Python中编写了不会按应有的方式释放内存的代码。 内存由Python占用,但即使不再使用也永远不会释放。 即使您使用ctrl + c中断正在运行的程序。 删除变量并运行gc.collect()似乎没有收集到的变量。 或与Ipython中相同,并运行%reset。 内存将不会被释放,并且运行gc.collect()无效。 我在Windows中进行了测试,因为我想看看它是否可能与垃圾收集器库一起使用。 看来是这样。 在Linux中然后在Windows中运行以下代码。 然后比较内存使用情况。 您将需要安装numpy和scipy。 在这个问题上的任何帮助或见解将不胜感激。

导入模型,创建一个实例,然后运行createSpecific()。

这是在Ubuntu 10.04中表现出这种行为的代码:

from numpy import array, maximum,intersect1d, meshgrid, std, log, log10, zeros, ones, argwhere, abs, arange, size, copy, sqrt, sin, cos, pi, vstack, hstack, zeros, exp, max, mean, savetxt, loadtxt,  minimum,  linspace,  where

from numpy.fft import fft

from scipy.stats import f_oneway, kruskal, sem, scoreatpercentile

#import matplotlib

#matplotlib.use('cairo.pdf')

from matplotlib.pyplot import plot, clf, show, cla, xlim, xscale, imshow, ylabel, xlabel, figure, savefig, close,  bar,  title,  xticks, yticks, axes, axis

from matplotlib.axes import Axes

from mpl_toolkits.mplot3d import Axes3D

#from enthought.mayavi import mlab

from matplotlib import cm

import matplotlib.pyplot as plt

import os

from time import clock

from timeit import Timer

class Model:

#Constructors and default includes

def __init__(self, prevAud = None,  debug=False):

if (prevAud == None):

self.fs=16000. #sample rate

self.lowFreq=60.

self.hiFreq=5000.

self.numFilt=300 #number of channel

self.EarQ = 9.26449   #9.26449

self.minBW = 24.7     #24.7

self.integrationWindow=.01

self.sliceAt=.035

self.maxOverallInhibit = 0.1

self.winLen = int(self.fs*self.integrationWindow+.01) #default integration window 10 ms

self.fullWind = 0.300

self.outShortWindow = None

self.siderArray = None

self.maxNormalizeValue = .284     # Optimized at .284

self.outputSemiModel = None

self.semitones = 11

self.activationTrace = None

return

def setErbScale(self, erbScale = None):

if (erbScale ==None):

self.erbScale = arange(100,500,5)

else:

self.erbScale = erbScale

def trainModel(self,soundVec=None, fs=None, lowfreq=None, highfreq=None, numfilt=None, figto=0, savefig = 'N', prompts=False, plotter=False):

self.setErbScale()

templateArray = self.intWindow(self.halfWaveRec(self.creGammatone(soundVec)))

for i in xrange(templateArray[0].size):

self.outerTest(self.innerTest(templateArray[:,i]))

return templateArray

def createSpecific(self, freqArray = None, semitones = 11, timeforHarm = .3, soundVec=None, fs=None, lowfreq=None, highfreq=None, numfilt=None, figto=0, saveData='N', fileDir='TempRunT/', prompts=False, plotter=False):

if (freqArray == None):

self.setErbScale()

freqArray = self.erbScale

if (type(semitones) == int):

semitones = arange(semitones+1)

totalRuns = int(timeforHarm/self.integrationWindow+.001)

inhibitWindowArray = zeros((freqArray.size,(semitones.size),self.numFilt,totalRuns))

for x in xrange(freqArray.size):

tempHarm = self.makeHarmonicAmpMod(freqArray[x],timeforHarm, numHarm=7,modulation=10)

for y in semitones:

tempChord = self.makeSemiChordAmpMod(tempHarm, freqArray[x],timeforHarm,modulation=10,numHarm=7,semi=y)

inhibitWindowArray[x,y] = self.trainModel( tempChord, savefig = 'N', plotter=plotter)

self.inhibitWindowArray = inhibitWindowArray

def creGammatone(self, soundVec):

temp = zeros((300,soundVec.size))

for i in xrange(temp[:,0].size):

temp[i] = -1**i*soundVec

return temp

def halfWaveRec(self, halfWaveFilts):

filtShape = halfWaveFilts.shape

if (filtShape[1] != int(self.fs*self.fullWind)):

halfWaveFilts = hstack((halfWaveFilts,zeros((self.numFilt,int(self.fs*self.fullWind)-filtShape[1]))))

temp = zeros((halfWaveFilts[:,0].size,halfWaveFilts[0].size))

halfWaveFilts = maximum(halfWaveFilts,temp)

del temp

return halfWaveFilts

def intWindow(self, integratedFilts):

winlen = self.winLen

length = integratedFilts[0].size/winlen

mod = integratedFilts[0].size%winlen

outShortWindow = zeros((integratedFilts[:,0].size,length))

meanval = 0

if (mod != 0):

for i in xrange(integratedFilts[:,0].size):

mean(integratedFilts[i,0:-mod].reshape(length,winlen),1,out=outShortWindow[i])

else:

for i in xrange(integratedFilts[:,0].size):

mean(integratedFilts[i].reshape(length,winlen),1,out=outShortWindow[i])

del integratedFilts

return outShortWindow

def innerTest(self, window):

temper = copy(window)

sider = 7

st = .04

sizer = temper.size

inhibVal = 0

for j in xrange(sider):

inhibVal = (temper[0:j+sider+1].sum())*(sider*2+1)/(sider+1+j)

window[j] += - st*(inhibVal)

for j in xrange(sider,sizer - sider):

inhibVal = temper[j-sider:j+sider+1].sum()

window[j] += - st*(inhibVal)

for j in xrange(sizer-sider, sizer):

inhibVal = (temper[j-sider:sizer].sum())*(sider*2+1)/(sider+sizer-j)

window[j] += - st*(inhibVal)

maxsub = max(window) * self.maxOverallInhibit

window += - maxsub

del temper

return window

def outerTest(self, window):

newSatValue = scoreatpercentile(window, (76))

numones = where(window > newSatValue)

window[numones]=1

self.maxSatValue = newSatValue

del numones

return window

def makeHarmonicAmpMod(self, freq = 100, time = 1.,modulation=10, fsamp=None, numHarm=7):

if fsamp == None: fsamp = self.fs

samples = arange(time*fsamp)

signal = 0

for x in xrange(1,(numHarm+1),1):

signal = signal + sin(samples/float(fsamp)*x*freq*2*pi)

signal = (signal)*maximum(zeros(time*fsamp),sin((samples/float(fsamp)*modulation*2*pi)))

return signal

def makeSemiChordAmpMod(self, harmVec = None, freq=100, time = 1.,  modulation=10, fsamp=None, numHarm=7, semi = 2):

if (harmVec == None): harmVec = self.makeHarmonicAmpMod(freq,time,modulation,fsamp,numHarm)

if (semi == 0): return harmVec

return harmVec + self.makeHarmonicAmpMod(freq*(2**(semi/12.)),time,modulation,fsamp,numHarm)

python分配的内存在后台使用malloc(),而mc.collect()所做的就我所知,即使调用了python,malloc()分配的内存也不会返回给系统。使用它自己的malloc(PyObject_malloc()和PyObject_Free()),但它们具有相同的行为,请看这里:stackoverflow.com/questions/2215259/

@singularity-我愿意尝试使用tcmalloc的说明来强制它释放对象,以查看其是否有效。您知道如何强制它使用该库。是否必须以特殊方式编译Python并将其链接到我假设的那个库?

@J Spen:看这里:pushtheweb.com/2010/06/python-and-tcmalloc,也许可以为您提供更多详细信息。

@singularity-我知道该链接,但是不确定如何使用TCMalloc为收集器编译Python,但是我已在此处张贴文章以查看它们是否会响应。

旁注,实际上不需要手动删除函数,类和代码中的局部变量。 python GC非常聪明,可以在不需要此变量时实现,并会自行对其进行排序。

IPython甚至在%reset之后都可以保留引用,除非您运行的开发版本应该在该版本中对其进行修复。看到这里:github.com/ipython/ipython/issues/141

@Jakob-我知道通常不需要del,但是我在调??试过程中添加了它们,因为我正在查看是否由于某种原因使引用保持完整并释放了内存。没有作用。

@Thomas K-有趣的笔记,但是使用%run时要阅读这些笔记。在Matplotlib完全支持Ipython 0.11的新功能之前,我不会运行开发版本,据我所知,该功能仍不完全兼容。另外,在普通的python shell中也会发生上述相同的问题。

@J Spen:我只是想提及一下,以防万一它使测试变得更加混乱。也可以保留没有%run的引用,但是由%run创建的引用最难追踪。

@J Spen-无论其价值如何,这似乎都不会泄漏我的机器上的内存(Python 2.7,numpy 1.6,OpenSuse 11.4,内核2.6.37.6)。

虚拟内存不是稀缺资源。 由于每个进程都有自己的地址空间,因此无需将其返回给系统。 你的实际问题是什么? 此行为导致您什么问题?

移动设备和开发板通常没有虚拟内存。 他们有RAM,通常就是这样。

@jww这是完全不真实的,甚至十年前都不是真实的。 您可能会发现没有虚拟内存的处理器作为诸如太阳能系统之类的电源控制器,也许还有一些很小的开发板(如arduino)。 就是这样。 我十年来没有见过没有虚拟内存的移动设备。

我的坏大卫。 它们具有虚拟内存,每个进程通常都有其自己的空间,但是它们没有交换文件。 您不能将这些设备当作台式机或服务器使用。 如果这样做,则会遇到很多内存不足的情况。 我似乎还记得在某些只有256 MB内存的早期iPad上,malloc的最大大小是32 MB或64 MB。

我安装了最新的numpy svn,问题消失了。 我假设它在numpy函数之一内部。 我从来没有机会进一步研究它。

<think>嗯,用户在使用PyMuPDF的page.get_pixmap方法后遇到了内存释放的问题,需要找到解决方案。首先,我需要回忆一下PyMuPDF的相关知识,尤其是关于内存管理的部分。 PyMuPDF处理图像生成时,get_pixmap会生成一个Pixmap对象,这个对象可能占用较大的内存,尤其是在高DPI或处理多页时。用户提到dpi、alpha参数,可能他们调整了这些参数导致内存使用增加。内存泄漏通常指对象未被正确释放,但有时候可能只是内存没有及时回收,看起来像泄漏。 接下来,我得考虑Python内存管理机制。Python有垃圾回收机制,但如果有循环引用或者对象被意外保留,可能导致内存不能及时释放。此外,Pixmap对象可能底层使用了C扩展,这部分内存管理可能需要显式释放。 解决方案可能包括:确保及时删除Pixmap对象,比如在不再需要时将其设为None或调用del。另外,使用上下文管理器或生成器来限制对象的作用域,帮助GC更快回收。还有,调整参数如dpi或关闭alpha通道来减少内存占用。此外,可能需要检查是否有其他引用导致对象无法释放。 另外,用户提到的page.get_pixmap的参数是否正确使用,比如是否在循环中重复生成Pixmap而没有及时释放。可能需要检查代码结构,确保每个Pixmap在处理后立即释放。 还有,查看PyMuPDF的文档是否有关于内存管理的建议,比如是否需要调用特定的清理方法。例如,某些库需要手动调用close()或清理函数。如果Pixmap有析构方法,确保其被正确调用。 另外,使用工具如tracemalloc或objgraph来跟踪内存泄漏的位置,确认问题是否出在get_pixmap本身,还是用户代码中存在未释放的引用。 最后,建议用户升级PyMuPDF到最新版本,因为可能已有相关修复。如果问题依旧,可能需要联系开发者或寻找替代方案。</think>### 解决PyMuPDF中`page.get_pixmap`内存释放问题 #### 1. 问题分析 `page.get_pixmap`生成的`Pixmap`对象会占用较多内存,尤其在处理高分辨率(高DPI)或包含透明通道(`alpha=True`)时。内存释放的可能原因包括: - **对象未显式释放**:Python垃圾回收(GC)可能未及时触发。 - **参数设置不当**:高DPI或启用`alpha`会增加内存占用。 - **循环引用或残留引用**:对象被意外保留导致无法回收[^1]。 --- #### 2. 解决方案 ##### 方法1:强制释放内存 显式删除`Pixmap`对象并触发垃圾回收: ```python import gc import fitz # PyMuPDF doc = fitz.open("file.pdf") page = doc.load_page(0) pix = page.get_pixmap(dpi=300, alpha=False) # 关闭alpha以减少内存 # ...处理图像... del pix # 显式删除对象 gc.collect() # 立即触发垃圾回收 ``` ##### 方法2:使用上下文管理 通过`with`语句限制对象作用域: ```python with fitz.open("file.pdf") as doc: page = doc.load_page(0) with page.get_pixmap(dpi=150) as pix: # 降低DPI # ...处理图像... # 退出作用域后自动释放 ``` ##### 方法3:优化参数 - **降低DPI**:如从300降至150可减少4倍内存。 - **关闭alpha通道**:设置`alpha=False`(除非需要透明度)。 ##### 方法4:分块处理大文件 逐页处理并立即释放资源: ```python for page_num in range(len(doc)): page = doc.load_page(page_num) pix = page.get_pixmap() # ...处理当前页... del pix gc.collect() ``` --- #### 3. 排查工具 - **内存分析**:使用`tracemalloc`追踪内存分配: ```python import tracemalloc tracemalloc.start() # ...运行代码... snapshot = tracemalloc.take_snapshot() top_stats = snapshot.statistics("lineno") for stat in top_stats[:10]: print(stat) ``` - **升级PyMuPDF**:确保使用最新版本(如1.23.0+),部分版本存在内存优化[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值