我的wxPython应用程序中潜在的内存泄漏

时间:2009-09-07 00:39:22

标签: image memory memory-leaks wxpython

我很确定我正在遭受内存泄漏,但我没有100%确定它是如何发生的。

我写的应用程序从URL下载2个图像,并将每组图像(称为事务)排队到队列中,由用户界面弹出并显示。图像非常大,平均大约2.5MB。因此,作为加速用户界面并使其响应更快的一种方法,我将每个事务图像预加载到wxImage对象中并存储它们。

当用户弹出另一个事务时,我将预加载的图像提供给一个窗口对象,然后将wxImage转换为位图,并将DC blit转换为窗口。然后窗口对象显示在面板上。

当用户完成事务时,我销毁窗口对象(可能是窗口消失,位图也是如此),事务数据结构被'None'覆盖。

但是,根据预先加载的图像数量,队列大小是否设置得很大并且是否一次完成,或者我是否让一个小的队列大小随着时间的推移而停止,它最终会崩溃。我真的不能让这件事发生.. :))

任何人都会看到我正在做的任何明显的逻辑错误? python垃圾收集?我不必处理内存问题。

[edit]这里是代码;)这是与下载图像的线程相关的代码 - 它在主线程中实例化运行GUI - 下载线程的主要功能是'fill_queue'功能:< / p>

def fill_queue(self):
    while True:
        if (self.len() < self.maxqueuesize):

            try:

                trx_data = self.download_transaction_data(self.get_url)

                for trx in trx_data:
                    self.download_transaction_images(trx)
                    if self.valid_images([trx['image_name_1'], trx['image_name_2']]):
                        trx = self.pre_load_images(trx)
                        self.append(trx)

            except IOError, error:
                print "Received IOError while trying to download transactions or images"
                print "Error Received: ", error
            except Exception, ex:
                print "Caught general exception while trying to download transactions or images"
                print "Error Received: ", ex

        else: 
            time.sleep(1)


def download_transaction_images(self, data):
    """ Method will download all the available images for the provided transaction """

    for(a, b) in data.items():
        if (b) and (a == "image_name_1" or a == "image_name_2"):
            modified_url   = self.images_url + self.path_from_filename(b)
            download_url   = modified_url + b
            local_filepath = self.cache_dir + b

            urllib.urlretrieve(download_url, local_filepath)
            urllib.urlcleanup()


def download_transaction_data(self, trx_location):
    """ Method will download transaction data and return a parsed list of hash structures """

    page = urllib.urlopen(trx_location)
    data = page.readlines()
    page.close()

    trx_list = []
    trx_data = {}
    for line in data:

        line = line.rstrip('|!\n')
        if re.search('id=', line):
            fields = re.split('\|', line)

            for jnd in fields:
                pairs = jnd.split('=')
                trx_data[pairs[0]] = pairs[1]

            trx_list.append(trx_data)

    return trx_list


def pre_load_images(self, trx):
    """ Method will create a wxImage and load it into memory to speed the image display """
    path1  = self.cache_dir + trx['image_name_1']
    path2  = self.cache_dir + trx['image_name_2']
    image1 = wx.Image(path1)
    image2 = wx.Image(path2)
    trx['loaded_image_1'] = image1
    trx['loaded_image_2'] = image2
    return trx        


def valid_images(self, images):
    """ Method verifies that the image path is valid and image is readable """
    retval = True
    for i in images:
        if re.search('jpg', i) or re.search('jpeg', i):
            imagepath = self.cache_dir + i
            if not os.path.exists(imagepath) or not wx.Image.CanRead(imagepath):
                retval = False
        else:
            retval = False

    return retval

此外,我想补充一点,有时候,在崩溃之前我在控制台中遇到特殊错误,它们看起来像是图像错误,但图像没有损坏,错误发生在所有图像的所有阶段。

  

申请转移太少   scanlines [2009-09-08 11:12:03]错误:   JPEG:无法加载 - 文件可能是   损坏。 [2009-09-08 11:12:11]   调试:.... \ src \ msw \ dib.cpp(134):   'CreateDIBSection'因错误而失败   0x00000000(操作完成   成功)。

这些错误可以单独发生,也可以一起发生。我认为发生的事情是,在某些时候,内存会被破坏,接下来发生的任何事情,如果我加载新的事务,图像,或者进行裁剪操作 - 都需要潜水。


所以不幸的是,在尝试将预加载函数调用wxImage移动到主gui线程的建议后,我仍然得到错误 - 再次发生在将太多图像加载到内存中或者如果它们位于记忆太久了。然后,当我尝试裁剪图像时,我得到一个内存错误 - 有些东西正在腐败,无论是在前一种情况下我使用太多还是没有足够的(这没有任何意义,因为我已经将我的页面文件大小增加到天文比例)或在后一种情况下,时间长度导致泄漏或腐败

我认为我现在能够使用的唯一方法是使用调试器 - 是否有任何简单的方法来调试wxPython应用程序?我想特别看看内存使用情况。

我认为我需要预加载图像的主要原因是因为如果我在每个图像上调用wxImage(我一次显示两个),每次加载“事务”时,从一个事务到下一个事务的接口非常慢和笨重 - 如果我把它们加载到内存中它非常快 - 但后来我得到了我的内存错误。

1 个答案:

答案 0 :(得分:2)

两个想法:

  1. 你没有提到下载是否正在运行一个单独的线程(实际上我现在看到它在一个单独的线程中运行,我应该仔细阅读)。我很确定wx.Image不是线程安全的,所以如果你在非GUI线程中实例化wx.Images,那可能会导致这样的麻烦。 (这几乎肯定是问题,大多数wx类/对象/函数都不是线程安全的。)
  2. 我之前在wxPython中被nasty IncRef/DecRef bugs咬了(由于底层的C ++绑定)(主要与wx.Grid及相关类相关联)。虽然我不知道wx.Image,但我发现你可能需要像wx.Grid中那样手动管理内存并不会让我感到惊讶。
  3. 修改 您需要在GUI线程中实例化wx.Image,而不是下载线程(您的上述代码看起来就像您当前在非GUI线程中实例化的那样)。通常,这几乎总会在任何GUI工具包中引起许多问题。在这种情况下,您可以在wxPython邮件列表中搜索大量电子邮件。我个人会这样做:

    1. 下载网址的队列。
    2. 线程下载图片。
    3. 让下载线程在一个单独的队列中放置磁盘位置(注意竞争条件!)并将自定义wx.Event(threadsafe)(带有wx.PostEvent函数的threadsafe)发布到App线程。
    4. 让GUI线程弹出文件位置并实例化wx.Image ----&gt; wx.Bitmap(可能在应用程序空闲时使用wx.CallAfter进行处理)
    5. 根据需要显示(Blit)。