2009-09-07 3 views
1

Я уверен, что я страдаю от утечки памяти, но у меня нет 100% прибитых вниз, как это происходит.Потенциальная утечка памяти в моем приложении wxPython

Приложение Iv'e записывает 2 изображения с URL-адреса и ставит в очередь каждый набор изображений, называемый транзакцией, в очередь, которая выставляется пользовательским интерфейсом и отображается. Изображения довольно большие, в среднем около 2,5 МБ. Таким образом, чтобы ускорить пользовательский интерфейс и сделать его более отзывчивым, я предварительно загружаю изображения транзакций в объекты wxImage и сохраняю их.

Когда пользователь выталкивает другую транзакцию, я загружаю предварительно загруженное изображение в объект окна, который затем преобразует wxImage в растровое изображение, а DC - к окну. Затем объект окна отображается на панели.

Когда транзакция завершена пользователем, я уничтожаю объект окна (предположительно, окно уходит, как и растровое изображение), а структура данных транзакции перезаписывается «Нет».

Однако, в зависимости от того, сколько изображений предварительно загружено, задан ли размер очереди и все сделано за один раз, или я могу позволить небольшому размеру очереди сидеть со временем, он в конечном итоге сбой. Я действительно не могу позволить этому случиться. :)

Кто-нибудь видит какие-либо очевидные логические ошибки в том, что они делают? Собирает ли мусор python? У меня нет большого опыта работы с проблемами памяти.

это код;) Это код, связанный с потоком, который загружает изображения - он задан в основном потоке, запускает GUI - основная функция потока загрузки - это функция «fill_queue»:

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 

Кроме того, я хотел бы добавить, что иногда, просто до аварии я получаю специфические ошибки в моей консоли, они выглядят как коррумпированные ошибки изображения, но изображения не повреждены, ошибка произошла на всех этапах на всех изображениях.

Application transferred too few scanlines [2009-09-08 11:12:03] Error: JPEG: Couldn't load - file is probably corrupted. [2009-09-08 11:12:11] Debug: ....\src\msw\dib.cpp(134): 'CreateDIBSection' fail ed with error 0x00000000 (the operation completed successfully.).

Эти ошибки могут произойти по-ла-карте или все вместе. Я думаю, что в какой-то момент память становится поврежденной и все, что происходит дальше, если я загружаю новую транзакцию или изображение или выполняю операцию обрезки - она ​​требует погружения.


Так, к сожалению, после того, как попробовать предложение перемещения вызова функции предварительной загрузки в wxImage в основной графический интерфейс нить я все еще получаю ошибку - снова это произойдет после того, как слишком много изображений были загружены в память или если они сидят в памяти слишком долго. Затем, когда я пытаюсь обрезать изображение, я получаю ошибку памяти - что-то развращает, будь то в первом случае я слишком много использую или не имею достаточного количества (что не имеет смысла, потому что я увеличил размер файла подкачки до астрономических пропорций) или в последнем случае, когда длительность времени вызывает утечку или повреждение

Единственный способ, я думаю, что я могу пойти на этом этапе - использовать отладчик - есть ли какие-либо простые способы отладки приложения wxPython? Я хотел бы видеть, в частности, использование памяти.

Основная причина, по которой я думаю, что мне нужно предварительно загрузить изображения, потому что, если я вызываю wxImage на каждое изображение (я показываю два за раз) каждый раз, когда я загружаю «транзакцию», интерфейс от одной транзакции к следующей очень медленный и неуклюжий - если я загружу их в память очень быстро, но тогда я получаю ошибку в памяти.

+0

Python делает сбор мусора, но если есть какие-либо ссылки, оставленные объекту, очевидно, что GC оставит его в покое. Так утечки памяти такого рода очень сильно зависят от того, как вы написали приложение. Одной небольшой ошибки в одной строке кода достаточно, чтобы сохранить ссылку и сохранить объект в живых. В целом, покажите нам код. –

+0

Если вы все еще получаете ошибку, я бы разместил ее в списке рассылки wxpython (группа google: wxpython-users) и посмотрел, поможет ли Робин Данн или другой гуру. Сожалею! – DrBloodmoney

+0

:) Хорошо. Хорошо, спасибо за предложение - он был твердый, но я должен делать что-то напуганное! – 2009-09-16 18:10:03

ответ

2

Две мысли:

  1. Вы не упоминаете, если загрузка выполняется в отдельном потоке (на самом деле сейчас я вижу, что это работает в отдельном потоке, я должен более внимательно прочитать). Я уверен, что wx.Image не является потокобезопасным, поэтому, если вы создаете wx.Images в потоке без GUI, это может привести к таким неприятностям. (Это почти наверняка проблема, большинство wx-классов/объектов/функций не являются потокобезопасными).
  2. Я был укушен nasty IncRef/DecRef bugs в wxPython (из-за привязок C++) до (в основном связанных с wx.Grid и ассоциированными классами). Хотя я не знаю никого с wx.Image, меня не удивит, если вы узнаете, что вам может потребоваться вручную управлять памятью, как это иногда бывает в wx.Grid.

Редактировать Вам нужно создать экземпляр wx.Image в GUI потоке, а не загрузки потока (который ваш приведенный выше код выглядит как вы в настоящее время инстанцировании в не-GUI потоке). В общем, это почти всегда вызовет множество проблем в любом наборе инструментов GUI. Вы можете найти список рассылки wxPython для большого количества писем, где это так. Лично я бы сделал это:

  1. Очередь для загрузки URL.
  2. Тема для скачивания картинок.
  3. Загрузите нить на место диска (обратите внимание на условия гонки!) В отдельной очереди и добавьте пользовательский wx.Event (threadsafe) (threadsafe с функцией wx.PostEvent) в поток приложений.
  4. Имейте поток GUI, чтобы указать местоположение файла и создать экземпляр wx.Image ----> wx.Bitmap (возможно, с wx.CallAfter для обработки, когда приложение не используется)
  5. Дисплей (Blit) по мере необходимости.
+0

Хорошо, это хорошее начало. Я нашел несколько мест в главном потоке графического интерфейса, где я копировал предварительно загруженный объект wxImage и поддерживал его до следующей транзакции - только для того, чтобы снова перезаписать его другой копией. Я не гений C++, но для меня это казалось отрывочным - поэтому я избавился от этого. Но его все еще происходит при более высоких размерах очереди - и, похоже, это происходит больше, когда доступная физическая память низкая. Любая идея, как вручную управлять памятью с помощью wxImage? Можно ли это сделать из python или мне нужно сделать ctypes или C++ расширение? – 2009-09-08 20:44:35

+0

Я отредактирую свой ответ, чтобы ответить на ваш комментарий :) – DrBloodmoney

+0

А, я вижу. Я даже не рассматривал использование CallAfter - то есть я думал, что мне пришлось выполнять предварительную загрузку в потоке загрузки, потому что мне нужно, чтобы они были загружены PRE, но теперь я вижу, что могу это сделать, когда основной поток GUI не занят, например, после отображения изображения и ввода пользователем. СПАСИБО - Я дам это выстрел :) – 2009-09-08 21:22:46

Смежные вопросы