2009-10-29 3 views
27

У меня есть программа Python, которая запускает серию экспериментов без данных, предназначенных для хранения из одного теста в другой. Мой код содержит утечку памяти, которую я полностью не могу найти (я смотрю на other threads на утечки памяти). Из-за ограничений по времени мне пришлось отказаться от обнаружения утечки, но если бы я смог изолировать каждый эксперимент, программа, вероятно, работала бы достаточно долго, чтобы получить нужные мне результаты.Python - Работа с утечками памяти

  • Будет ли выполняться каждый тест в отдельном потоке справки?
  • Существуют ли какие-либо другие способы изоляции последствий утечки?

Подробная информация о конкретной ситуации

  • Мой код состоит из двух частей: эксперимент полоз и фактический код эксперимента.
  • Несмотря на то, что между кодом для запуска всех экспериментов и кодом, используемым каждым экспериментом, не используются общие глобальные подпрограммы, некоторые классы/функции обязательно разделяются.
  • Экспериментальный бегун - это не просто простой цикл, который можно легко поместить в сценарий оболочки. Сначала он решает о тестах, которые необходимо выполнить с учетом параметров конфигурации, а затем запускает тесты, а затем выводит данные определенным образом.
  • Я попытался вручную вызова сборщика мусора в случае вопрос был просто, что сбор мусора не запустить, но это не сработало

Update

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

+2

Определите «утечку памяти» в python. – hasen

+0

Я имею в виду, вы не можете «забыть», чтобы освободить память; это GCed. – hasen

+0

Как вы можете сказать, что у вас есть утечка памяти? Разве ваша память процессов растет до больших размеров и никогда не сжимается? Если это так, имейте в виду, что Python не обязательно возвращает память в ОС только потому, что она больше не использует ее. –

ответ

49

Вы можете использовать что-то вроде этого, чтобы помочь отследить утечек памяти

>>> from collections import defaultdict 
>>> from gc import get_objects 
>>> before = defaultdict(int) 
>>> after = defaultdict(int) 
>>> for i in get_objects(): 
...  before[type(i)] += 1 
... 

теперь предположим, что тесты утечки некоторую память

>>> leaked_things = [[x] for x in range(10)] 
>>> for i in get_objects(): 
...  after[type(i)] += 1 
... 
>>> print [(k, after[k] - before[k]) for k in after if after[k] - before[k]] 
[(<type 'list'>, 11)] 

11, потому что мы просочились один список, содержащий более 10 списков

+1

Вау, это очень полезно. Хотя, это действительно относится к одному из способов, как найти утечки памяти. – Casebash

+1

Стоит ли делать сборку мусора перед тем, как сравнивать объекты? – Casebash

+0

Спасибо, мне удалось использовать это, чтобы решить проблему. – Casebash

2

Я бы просто реорганизовал эксперименты на отдельные функции (если это уже не так), то примите номер эксперимента из командной строки, который вызывает функцию одного эксперимента.

Праведный Bodgy вверх сценарий оболочки следующим образом:

#!/bin/bash 

for expnum in 1 2 3 4 5 6 7 8 9 10 11 ; do 
    python youProgram ${expnum} otherParams 
done 

Таким образом, вы можете оставить большую часть вашего кода как есть, и это уберет любые утечки памяти вы думаете, что между каждым экспериментом ,

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

Хотя трудно представить себе утечку памяти в Python, я возьму ваше слово на этом - вы можете хотя бы рассмотреть возможность того, что вы ошибаетесь там. Подумайте, поднимая , что в отдельном вопросе, с чем мы можем работать с низким приоритетом (в отличие от этой быстрой версии).

Обновление: Создание сообщества wiki, так как вопрос несколько изменился с оригинала.Я бы удалил ответ, но по-прежнему считаю, что это полезно - вы могли бы сделать то же самое с вашим экспериментальным бегуном, как я предложил сценарий bash, вам просто нужно убедиться, что эксперименты являются отдельными процессами, так что утечки памяти не происходят (если утечка памяти находится в бегуне, вам придется делать анализ основных причин и исправлять ошибку).

+0

Я действительно рассматривал возможность написания сценария оболочки, но, к сожалению, мой экспериментальный код намного сложнее, чем этот – Casebash

+1

@paxdiablo: Я согласен, что этот ответ должен быть оставлен, поскольку это может быть полезно для всех, кто посещает этот вопрос. – Casebash

4

Нити не помогли бы. Если вы должны отказаться от обнаружения утечки, то единственным решением, которое будет содержать его эффект, является запуск нового процесса (например, когда тест оставил общее потребление памяти слишком высоким по своему усмотрению - вы можете определить VM легко, читая /proc/self/status в Linux и другие аналогичные подходы к другим ОС).

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

Или, более прочно, убедитесь, что по мере завершения каждого теста его идентификация добавляется к некоторому файлу с известным именем. Когда программа запускается, она начинается с чтения этого файла и, таким образом, знает, какие тесты уже выполнены. Эта архитектура более прочная, поскольку она также охватывает случай, когда программа выдает во время теста; конечно, чтобы полностью автоматизировать восстановление после таких сбоев, вы захотите, чтобы отдельная программа сторожевого таймера и процесс отвечали за запуск нового экземпляра тестовой программы, когда он определил, что предыдущий разбился (он мог использовать subprocess для этой цели - ему также нужно указать, когда последовательность будет закончена, например, обычный выход из тестовой программы может означать, что, хотя любой сбой или выход со статусом! = 0 означают необходимость запуска нового нового экземпляра).

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

+0

Спасибо, что кучи для предложение, но мне удалось найти утечку – Casebash

2

У меня была такая же проблема с третьей библиотекой C, которая протекала. Самая чистая работа, о которой я могла думать, - это развить и подождать. Преимущество этого заключается в том, что вам даже не нужно создавать отдельный процесс после каждого прогона. Вы можете определить размер вашей партии.

Вот общее решение (если вы когда-нибудь найти утечку, единственное изменение, которое вам нужно сделать, это изменить пробег() для вызова run_single_process() вместо run_forked(), и вы будете делать):

import os,sys 
batchSize = 20 

class Runner(object): 
    def __init__(self,dataFeedGenerator,dataProcessor): 
     self._dataFeed = dataFeedGenerator 
     self._caller = dataProcessor 

    def run(self): 
     self.run_forked() 

    def run_forked(self): 
     dataFeed = self._dataFeed 
     dataSubFeed = [] 
     for i,dataMorsel in enumerate(dataFeed,1): 
      if i % batchSize > 0: 
       dataSubFeed.append(dataMorsel) 
      else: 
       self._dataFeed = dataSubFeed 
       self.fork() 
       dataSubFeed = [] 
       if self._child_pid is 0: 
        self.run_single_process() 
       self.endBatch() 

    def run_single_process(self) 
     for dataMorsel in self._dataFeed: 
      self._caller(dataMorsel) 

    def fork(self): 
     self._child_pid = os.fork() 

    def endBatch(self): 
     if self._child_pid is not 0: 
      os.waitpid(self._child_pid, 0) 
     else: 
      sys.exit() # exit from the child when done 

Это изолирует утечку памяти дочернему процессу. И он никогда не будет течь больше раз, чем значение переменной batchSize.

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