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

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

  1. чтение входного файла
  2. обработайте файл и создайте список треугольников, представленных их вершинами
  3. вывод вершин в формате OFF: список вершин, за которым следует список треугольников. Треугольники представлены индексами в списке вершин

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

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

9 ответов


по данным Официальная Документация Python, вы можете заставить сборщик мусора освободить неиспользуемую память с gc.collect(). Пример:

import gc
gc.collect()

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

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

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


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

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

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


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

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

вы не можете явно освобождать память. Что вам нужно сделать, так это убедиться, что вы не сохраняете ссылки на объекты. Тогда они будут отбросами, освобождающими память.

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

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


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

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


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

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

тем не менее, есть некоторые инструменты, чтобы помочь с этим:


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

чтобы исправить проблемы с памятью и производительностью, я переключился на использование многопоточного трюка, который я где-то читал (извините, я больше не могу найти связанный пост). Прежде чем я читал каждую строку файла в big 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

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