Как я могу явно освободить память в Python?

Я написал программу Python, которая действует на большой файл ввода, чтобы создать несколько миллионов объектов, представляющих треугольники. Алгоритм:

  • прочитать входной файл
  • обработать файл и создать список треугольников, представленных их вершинами
  • выводит вершины в формате ВЫКЛ: список вершин, за которым следует список треугольников. Треугольники представлены индексами в список вершин

Требование OFF, чтобы я распечатывал полный список вершин, прежде чем печатать треугольники, означает, что я должен удерживать список треугольников в памяти, прежде чем я напишу вывод в файл. Между тем я получаю ошибки памяти из-за размеров списков.

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

Ответ 2

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

Единственный надежный способ гарантировать, что большое, но временное использование памяти возвращает все ресурсы в систему, когда это делается, заключается в том, чтобы это использование происходило в подпроцессе, который завершает работу с голосом памяти. В таких условиях операционная система БУДЕТ выполнять свою работу и с радостью перерабатывает все ресурсы, которые мог обработать подпроцесс. К счастью, модуль multiprocessing делает такую ​​работу (которая была скорее больной) не так уж плоха в современных версиях Python.

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

Ответ 3

Утверждение del может быть полезным, но IIRC не гарантирует освобождение памяти. Документы здесь... и почему он не выпущен здесь.

Я слышал, как люди на Linux и Unix-типа системы, нарисовывая процесс python, выполняют некоторую работу, получая результаты, а затем убивая ее.

В этой статье есть заметки о сборщике мусора Python, но я думаю, что недостаток управления памятью является недостатком управляемой памяти

Ответ 4

Python собирает мусор, поэтому, если вы уменьшите размер своего списка, он восстановит память. Вы также можете использовать оператор "del", чтобы полностью избавиться от переменной:

biglist = [blah,blah,blah]
#...
del biglist

Ответ 5

Вы не можете явно освободить память. Что вам нужно сделать, так это убедиться, что вы не держите ссылки на объекты. Затем они будут собирать мусор, освобождая память.

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

http://www.prasannatech.net/2009/07/introduction-python-generators.html

Ответ 6

(Свободная память периодически выполняется автоматически. del может быть таким образом вашим другом, поскольку он помещает объекты как удаляемые.)

Возможно, вы не столкнетесь с какой-либо проблемой памяти в первую очередь, используя более компактную структуру для своих данных. Таким образом, списки номеров намного менее эффективны для памяти, чем формат, используемый стандартным модулем array или сторонним модулем numpy. Вы сохранили бы память, поместив ваши вершины в массив NumPy 3xN и ваши треугольники в N-элементном массиве.

Ответ 7

Другие опубликовали несколько способов, чтобы вы могли "уговорить" интерпретатор Python освобождать память (или иным образом избегать проблем с памятью). Скорее всего, вы должны сначала попробовать свои идеи. Однако я считаю важным дать вам прямой ответ на ваш вопрос.

На самом деле нет никакого способа напрямую сообщить Python о свободной памяти. Дело в том, что если вы хотите, чтобы низкий уровень контроля, вам придется писать расширение на C или С++.

Тем не менее, есть некоторые инструменты, которые помогут в этом:

Ответ 8

У меня была аналогичная проблема при чтении графика из файла. Обработка включала вычисление матрицы с плавающей запятой 200 000 x 200 000 (по одной строке за раз), которая не вписывалась в память. Попытка освободить память между вычислениями, используя gc.collect() зафиксировала связанный с памятью аспект проблемы, но это привело к проблемам с производительностью: я не знаю, почему, но хотя объем используемой памяти оставался постоянным, каждый новый вызов gc.collect() заняло больше времени, чем предыдущее. Поэтому довольно быстро сбор мусора занимал большую часть времени вычислений.

Чтобы исправить проблемы с памятью и производительностью, я переключился на использование многопоточного трюка, который я когда-то читал (извините, я больше не могу найти связанный пост). Прежде чем я читал каждую строку файла в большом for цикла обработки, и работаю gc.collect() каждый раз, и некоторое время, чтобы освободить пространство памяти. Теперь я вызываю функцию, которая читает и обрабатывает фрагмент файла в новом потоке. Как только поток заканчивается, память автоматически освобождается без странной проблемы с производительностью.

Практически это работает так:

from dask import delayed  # this module wraps the multithreading
def f(storage, index, chunk_size):  # the processing function
    # read the chunk of size chunk_size starting at index in the file
    # process it using data in storage if needed
    # append data needed for further computations  to storage 
    return storage

partial_result = delayed([])  # put into the delayed() the constructor for your data structure
# I personally use "delayed(nx.Graph())" since I am creating a networkx Graph
chunk_size = 100  # ideally you want this as big as possible while still enabling the computations to fit in memory
for index in range(0, len(file), chunk_size):
    # we indicates to dask that we will want to apply f to the parameters partial_result, index, chunk_size
    partial_result = delayed(f)(partial_result, index, chunk_size)

    # no computations are done yet !
    # dask will spawn a thread to run f(partial_result, index, chunk_size) once we call partial_result.compute()
    # passing the previous "partial_result" variable in the parameters assures a chunk will only be processed after the previous one is done
    # it also allows you to use the results of the processing of the previous chunks in the file if needed

# this launches all the computations
result = partial_result.compute()

# one thread is spawned for each "delayed" one at a time to compute its result
# dask then closes the tread, which solves the memory freeing issue
# the strange performance issue with gc.collect() is also avoided

Ответ 9

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