Python - Обойти утечки памяти
У меня есть программа на Python, которая проводит серию экспериментов без данных, предназначенных для хранения от одного теста к другому. Мой код содержит утечку памяти, которую я совершенно не могу найти (я смотрел другие потоки на утечки памяти). Из-за нехватки времени мне пришлось отказаться от поиска утечки, но если бы мне удалось изолировать каждый эксперимент, программа, вероятно, работала бы достаточно долго, чтобы получить нужные мне результаты.
- Поможет ли запуск каждого теста в отдельном потоке?
- Существуют ли другие методы выявления последствий утечки?
Подробно о конкретной ситуации
- Мой код состоит из двух частей: бегун эксперимента и фактический код эксперимента.
- Хотя между кодом для выполнения всех экспериментов и кодом, используемым в каждом эксперименте, не используются общие глобальные значения, некоторые классы / функции обязательно являются общими.
- Организатор эксперимента - это не просто цикл for, который можно легко вставить в сценарий оболочки. Сначала он определяет тесты, которые необходимо выполнить с учетом параметров конфигурации, затем запускает тесты, а затем выводит данные определенным образом.
- Я попытался вручную вызвать сборщик мусора на тот случай, если проблема заключалась в том, что сборка мусора не запускалась, но это не сработало
Обновить
Ответ Гнибблера фактически позволил мне выяснить, что мои объекты ClosenessCalculation, в которых хранятся все данные, использованные во время каждого вычисления, не уничтожаются. Затем я использовал это, чтобы вручную удалить некоторые ссылки, которые, кажется, исправили проблемы с памятью.
4 ответа
Вы можете использовать что-то вроде этого, чтобы помочь отследить утечки памяти
>>> 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 списков
Темы не помогут. Если вам нужно отказаться от поиска утечки, то единственное решение, сдерживающее ее влияние, - это время от времени запускать новый процесс (например, когда в результате теста общее потребление памяти слишком велико для вас - вы можете определить размер виртуальной машины). легко читать /proc/self/status
в Linux и других подобных подходах на других ОС).
Убедитесь, что общий сценарий принимает необязательный параметр, чтобы сообщить ему, с какого номера теста (или другого идентификатора теста) начинаться, чтобы, когда один экземпляр сценария решает, что он занимает слишком много памяти, он может сообщить своему преемнику, откуда начинать перезапуск.,
Или, что еще более важно, убедитесь, что после завершения каждого теста его идентификация добавляется в некоторый файл с известным именем. Когда программа запускается, она начинается с чтения этого файла и, таким образом, знает, какие тесты уже были выполнены. Эта архитектура более надежна, потому что она также охватывает случай, когда программа падает во время теста; конечно, чтобы полностью автоматизировать восстановление после таких сбоев, вам нужно, чтобы отдельная сторожевая программа и процесс отвечали за запуск нового экземпляра тестовой программы, когда он определит, что предыдущий сбой (он может использовать subprocess
для этой цели - ему также нужен способ узнать, когда последовательность завершена, например, обычный выход из тестовой программы может означать, что при любом сбое или выходе с состоянием!= 0 означает необходимость запуска нового свежего экземпляра),
Если эти архитектуры привлекательны, но вам нужна дополнительная помощь в их реализации, просто прокомментируйте этот ответ, и я буду рад предоставить пример кода - я не хочу делать это "превентивно", если есть еще невыраженные проблемы что делает архитектуру непригодной для вас. (Это также может помочь узнать, на каких платформах вам нужно работать).
У меня была та же проблема со сторонней библиотекой C, которая протекала. Самым чистым решением, которое я мог придумать, было форкнуть и ждать. Преимущество этого в том, что вам даже не нужно создавать отдельный процесс после каждого запуска. Вы можете определить размер вашей партии.
Вот общее решение (если вы обнаружите утечку, единственное изменение, которое вам нужно сделать, это изменить run() на вызов 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.
Я бы просто реорганизовал эксперименты в отдельные функции (если уже не так), а затем принял бы номер эксперимента из командной строки, который вызывает одну функцию эксперимента.
Сценарий оболочки выглядит так:
#!/bin/bash
for expnum in 1 2 3 4 5 6 7 8 9 10 11 ; do
python youProgram ${expnum} otherParams
done
Таким образом, вы можете оставить большую часть своего кода как есть, и это устранит любые утечки памяти, которые, по вашему мнению, возникают между каждым экспериментом.
Конечно, лучшее решение - это всегда найти и устранить причину проблемы, но, как вы уже заявили, это не вариант для вас.
Хотя трудно представить утечку памяти в Python, я верю на ваше слово - вы, возможно, захотите, по крайней мере, рассмотреть возможность того, что вы там ошиблись. Рассмотрим этот вопрос в отдельном вопросе, над которым мы можем работать с низким приоритетом (в отличие от этой версии с быстрым исправлением).
Обновление: создание сообщества вики, поскольку вопрос несколько изменился по сравнению с оригиналом. Я бы удалил ответ, но, тем не менее, я думаю, что он по-прежнему полезен - вы могли бы сделать то же самое со своим организатором эксперимента, поскольку я предложил сценарий bash, вам просто нужно убедиться, что эксперименты являются отдельными процессами, чтобы не происходили утечки памяти (если утечки памяти у бегуна, вам придется выполнить анализ основных причин и исправить ошибку правильно).