Почему когда python завершает работу освобождается не вся память

Обновлено: 03.07.2024

у меня есть несколько вопросов относительно использования памяти в следующем примере.

если я бегу в интерпретаторе,

реальная память, используемая на моей машине, доходит до 80.9mb . Я тогда,

реальная память идет вниз, но только до 30.4mb . Интерпретатор использует 4.4mb baseline Итак, в чем преимущество не выпускать 26mb памяти для ОС? Это потому, что Python "планирует вперед", думая, что вы можете использовать опять столько воспоминаний?

почему он выпускает 50.5mb в частности - какова сумма, которая выделяется на основе?

есть ли способ заставить Python освободить всю используемую память (если вы знаете, что больше не будете использовать столько памяти)?

память, выделенная в куче, может быть подвержена отметкам прилива. Это осложняется внутренней оптимизацией Python для выделения небольших объектов ( PyObject_Malloc ) в 4 пулах KiB, классифицированных для размеров распределения в кратных 8 байтам -- до 256 байт (512 байт в 3.3). Сами бассейны находятся в 256 КИБ аренах, поэтому, если используется только один блок в одном пуле, вся арена 256 КИБ не будет выпущена. В Python 3.3 распределитель малых объектов был переключен на использование анонимных карт памяти вместо кучи, поэтому он должен работать лучше при освобождении памяти.

кроме того, встроенный в видах поддержания freelists ранее выделенных объектов, которые могут или не могут использовать небольшой объект выделения. The int тип поддерживает freelist аппликации с собственной выделенной памяти, и очистка его требует вызова PyInt_ClearFreeList() . Это можно вызвать косвенно, выполнив полное gc.collect .

попробуйте вот так, и скажите мне, что вы получите. Вот ссылка для psutil.

я переключился на измерение относительно размера VM процесса, чтобы исключить влияние других процессов в системе.

среда выполнения C (например, glibc, msvcrt) сжимает кучу, когда непрерывное свободное пространство вверху достигает постоянного, динамического или настраиваемого порога. С glibc вы можете настроить это с помощью mallopt (M_TRIM_THRESHOLD). Учитывая это, это не удивительно, если куча сжимается больше, даже намного больше, чем блок, что вы free .

в 3.x range не создает список, поэтому тест выше не создаст 10 миллионов int объекты. Даже если и так, то . --3--> тип в 3.x-это в основном 2.x long , которые не реализовать freelist аппликации.

Я предполагаю, что вопрос, который вас действительно волнует здесь:

есть ли способ заставить Python освободить всю используемую память (если вы знаете, что больше не будете использовать столько памяти)?

нет, нет. Но есть простой обходной путь: дочерние процессы.

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

это не совсем тривиально и бесплатно, но это довольно легко и дешево, что обычно достаточно для того чтобы быть стоящим.

во-первых, самый простой способ создать дочерний процесс - с concurrent.futures (или, для 3.1 и более ранних версий, futures backport на PyPI):

Если вам нужно немного больше контроля, используйте multiprocessing модуль.

  • запуск процесса происходит медленно на некоторых платформах, особенно Windows. Мы говорим о миллисекундах, а не о минутах, и если вы заставите одного ребенка работать 300 секунд, вы даже не заметите этого. Но это не бесплатно.
  • если большой объем временной памяти, который вы используете, действительно большой, это может привести к замене вашей основной программы. Из конечно, вы экономите время в долгосрочной перспективе, потому что, если эта память будет висеть вечно, это должно привести к замене в какой-то момент. Но это может превратить постепенную медлительность в очень заметные все сразу (и ранние) задержки в некоторых случаях использования.
  • передачу больших объемов данных между процессами может быть медленным. Опять же, если вы говорите о отправке 2K аргументов и получении 64K результатов, вы даже не заметите этого, но если вы отправляете и получаете большое количество data, вы захотите использовать какой-то другой механизм (файл, mmap ped или иначе; API общей памяти в multiprocessing ; etc.).
  • отправка больших объемов данных между процессами означает, что данные должны быть pickleable (или, если вы вставляете их в файл или общую память, struct -в состоянии или в идеале ctypes -able).

Эриксон ответил на вопрос №1, и я ответил на вопрос №3 (Оригинал №4), но теперь давайте ответим на вопрос № 2:

почему он выпускает 50.5 mb, в частности, - какова сумма, которая выпущена на основе?

то, на чем он основан, в конечном счете, целая серия совпадений внутри Python и malloc Это очень трудно предсказать.

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

или вы можете измерять страницы в использовании, которые могут или не могут рассчитывать выделенные, но никогда не тронутые страницы (в системах, которые оптимистично выделяют, например linux), страницы, которые выделены, но помечены MADV_FREE , etc.

если вы действительно измеряете выделенные страницы (что на самом деле не очень полезно делать, но, похоже, это то, о чем вы спрашиваете), и страницы действительно были освобождены, два обстоятельства, при которых это может произойти: либо вы использовали brk или эквивалент сокращения сегмента данных (очень редко в настоящее время), или вы использовали munmap или аналогично выпуску сопоставленного сегмента. (Есть также теоретически незначительный вариант последнего, в том, что есть способы освободить часть отображенного сегмента-например, украсть его с MAP_FIXED на MADV_FREE сегмент, который вы сразу unmap.)

но большинство программ непосредственно не выделяют вещи из страниц памяти; они используют malloc -стиль выделения. Когда вы звоните free , распределитель может выпускать страницы в ОС только в том случае, если вы просто free ing последний живой объект в отображении (или на последних N страницах сегмента данных). Нет никакого способа, которым ваше приложение может разумно предсказать это или даже обнаружить, что это произошло заранее.

CPython делает это еще более сложным-он имеет пользовательский 2-уровневый распределитель объектов поверх пользовательского распределителя памяти поверх malloc . (См.Комментарии Источник для более подробного объяснения.) И, кроме того, даже на уровне API C, а тем более Python, вы даже не контролируете напрямую, когда объекты верхнего уровня освобождаются.

Итак, когда вы выпускаете объект, как вы знаете, собирается ли он освободить память для ОС? Ну, сначала вы должны знать, что вы выпустили последнюю ссылку (включая любые внутренние ссылки, о которых вы не знали), позволяя GC освободить его. (В отличие от других реализаций, по крайней мере CPython будет освобождать объект, как только это разрешено.) Это обычно освобождает по крайней мере две вещи на следующем уровне (например, для строки вы освобождаете PyString объект и строковый буфер).

если вы do освободить объект, чтобы узнать, приводит ли это к следующему уровню вниз, чтобы освободить блок хранения объекта, вы должны знать внутреннее состояние распределителя объектов, а также то, как он реализован. (Очевидно, это не может произойти, если вы не освобождаете последнее в блоке, и даже тогда это может не произойти.)

если вы do освободить блок хранения объектов, чтобы узнать, вызывает ли это free вызов, вы должны знать внутреннее состояние распределителя PyMem, а также как он реализован. (Опять же, вы должны освободить последний используемый блок в malloc область ed, и даже тогда это может не произойти.)

если вы do free a malloc область ed, чтобы узнать, вызывает ли это munmap или эквивалент (или brk ), вы должны знать внутреннее состояние malloc , а также как это реализовано. И этот, в отличие от других, очень специфичен для платформы. (И опять же, вы обычно должны освобождать последнее в использовании malloc внутри mmap сегмент, и даже тогда он может не случаться.)

Итак, если вы хотите понять, почему это случилось, чтобы освободить ровно 50,5 Мб, вам придется отслеживать его снизу вверх. Почему?!--0--> unmap 50.5 mb стоит страниц, когда вы сделали эти один или несколько free вызовы (возможно, немного больше, чем 50.5 mb)? Вы должны прочитать malloc , а затем пройти различные таблицы и списки, чтобы увидеть его текущее состояние. (На некоторых платформах он может даже использовать информацию системного уровня, которая в значительной степени невозможно захватить без создания моментального снимка системы для проверки в автономном режиме, но, к счастью, это обычно не проблема.) И затем вы должны сделать то же самое на 3 уровня выше.

Итак, единственный полезный ответ на вопрос "что."

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

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

Я хочу быть ясным, я не вижу поведения, описанного в этом вопросе. Вместо этого мой вопрос касается самого вопроса:

Почему не освобождается вся память при выходе CPython?

И дает этот ответ:

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

Если вы хотите заставить Python удалять определенные вещи при освобождении, используйте модуль atexit для запуска функции, которая заставит эти удаления.

Это означает, что операционная система с управляемой памятью (Linux, Mac, Windows, GNU, BSD, Solaris. ) звучит как полная глупость.

На выходе программы (будь то Python или что-либо еще) любая память, которую она запрашивает из ОС, освобождается (поскольку ОС имеет контроль над таблицами виртуальной страницы и т.д. И т.д.). Программе не нужно выделять или деконструировать что-либо (что-то, что нужно было делать, как было подчеркнуто к тому времени, когда кто-то использовал cp столкнулся с деконструкцией хеш-таблицы), но я не думаю, что какая-либо ОС "Поддержка Python 3 ставит это требование на программы.

Это имеет смысл в каком-то контексте, о котором я не знаю? Что это значит?

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

Если вы запускаете cpython на любой нормальной серверной/настольной ОС, которая освобождает всю память процесса, когда она выйдет, тогда нет проблем с утечками памяти. ОС заботится об освобождении всей памяти при выходе из процесса.

Часто задаваемые вопросы больше информируют вас о том, что cpython не вызывает free() или подобное во всей памяти, которую он выделил с помощью malloc() или аналогичного. Это может иметь последствия, если вы запускаете cpython на ОС, которая не освобождает всю память, полученную процессом, когда процесс завершается (эти операционные системы существуют, в частности, это связано со многими встроенными ядрами). И если вы запустите cpython под детектором профилей/утечки памяти, этот детектор может сообщить об ошибке памяти() не как утечку.


Ни одна компьютерная программа не может работать без данных. А данные, чтобы программа имела к ним доступ, должны располагаться в оперативной памяти вашего компьютера. Но что такое оперативная память на самом деле? Когда произносишь это словосочетание, многие сразу представляют «железную» плашку, вставленную в материнскую плату, на которой написано что-то типа 16Gb DDR4 2666MHz. И они, разумеется, правы — это действительно физический блок оперативной памяти, в котором, в итоге, все данные и оказываются. Но прежде, чем стать доступной внутри вашей программы, на память (как и на всё остальное аппаратное обеспечение) накладывается куча абстракций.

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

Во-вторых, внутри операционной системы, есть абстракция, называемая «процесс», которая, в том числе, предназначена для изоляции и упрощения работы с ресурсами отдельными программами. Именно процесс «делает вид», что независимо от количества запущенных программ и объема установленной в компьютер оперативной памяти, вашей программе доступно 4 ГБ RAM (на 32-разрядных системах) или сильно больше (на 64-разрядных).

Долгое время, компьютерным программам, хватало этого набора абстракций. Вам нужны данные, вы хотите с ними поработать? Всё просто:

Попросите у процесса (ОС) выделить вам немного (одну или несколько страниц) оперативной памяти.

Поработайте с ней.

Верните ее в операционную систему.

Такой подход позволял работать с памятью максимально эффективно. Вы точно знали, сколько реально вам памяти выделено, зачем вы ее используете, и т.д. и т.п. Однако помимо преимуществ подход имеет и ряд недостатков. Ключевой из них — сложность. Управлять памятью вручную — это сложно и тяжело. Легко забыть что-то удалить или вернуть страницу операционной системе, и сразу возникает утечка: программа держит неиспользуемую память просто так, не давая применять ее для решения других задач.

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

Механизм памяти в Python

Python — это язык с управляемой памятью. Причем для управления ею он использует несколько механизмов. Я постараюсь коротко осветить основные из них. Для зануд сразу подчеркну, что под Python’ом я понимаю конкретную реализацию CPython, а не какие-то другие версии, в которых всё может работать по-другому :)

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

Оставшаяся свободная виртуальная память может использоваться для хранения информации об объектах Python’а. Для управления этой памятью в CPython используется специальный механизм, который называется аллокатор. Он используется каждый раз, когда вам нужно создать новый объект.

Обычно мы в своих программах не оперируем большими объектами. Большая часть наших данных — это числа, строки и т.п., они занимают не такой уж большой объем в расчёте на одно значение. Но зато мы создаем их достаточно часто. И это приводило бы к проблемам, если бы Python абсолютно все такие вызовы транслировал в операционную систему. Системный вызов на выделение памяти — штука трудозатратная, зачастую связанная с переходом в контекст ядра операционной системы и т.п. Поэтому одна из главных задач аллокатора Python — оптимизация количества системных вызовов.

Ремарка. Для больших объектов (больше 512 байт) Python выделяет память напрямую у ОС. Обычно таких объектов не очень много в рамках программы, и создаются они нечасто. Поэтому накладные расходы на создание таких объектов напрямую в RAM не так высоки.

Как же устроен аллокатор внутри? Он состоит из частей трёх видов:

Арена — большой непрерывный кусок памяти (обычно 256 килобайт), содержит несколько страниц виртуальной памяти операционной системы.

Пул — одна страница виртуальной памяти (обычно 4 килобайта).

Блок — маленький кусочек памяти, используемый для хранения одного объекта.

Давайте поговорим о них подробнее. Когда Python’у нужно расположить какой-то объект в оперативной памяти он ищет подходящую арену. Если такой нету, он запрашивает новую арену у операционной системы. Что значит «подходящую»? Арены организованы в двусвязный список и отсортированы от самой заполненной к самой свободной. Для добавления нового объекта выбирается САМАЯ ЗАПОЛНЕННАЯ арена. Почему такая странная логика? Это связано с политикой освобождения памяти. Арены — единственное место, в котором происходит запрос и освобождение памяти в Python. Если какая-то арена полностью освобождается от объектов, она возвращается назад операционной системе и память освобождается. Таким образом, чем компактнее живут объекты в рамках арен, тем ниже общее потребление оперативной памяти программой.

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

Каждый пул может быть в одном из трех состояний — used, full и empty. Full означает, что пул полностью занят и не может принять новые объекты. Empty — что пул полностью пустой и может быть использован для хранения новых объектов (при этом пустой пул может быть использован для хранения объектов любого размера). Used — это пул, который используется для хранения объектов, но при этом еще не заполнен полностью.

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

Внутри пула живут блоки. Каждый блок предназначен для хранения одного объекта. В целях унификации размеры блоков фиксированы. Они могут быть размером 8, 16, 24, 32 …. 512 байт. Если Вам нужно 44 байта для хранения объекта, то он будет расположен в блоке на 48 байт. Каждый пул содержит список свободных и занятых блоков (на самом деле, есть еще untouched-блоки, в которых никогда не жили данные, но по сценариям использования они похожи на free-блоки, поэтому не будем на них останавливаться подробно). Когда вы хотите разместить новый объект, берется первый свободный блок, и объект располагается в нем. Когда объект удаляется, его блок помещается в список свободных.

Время жизни объекта и при чем тут GIL

Поговорив о том, как происходит выделение и освобождение памяти в Python, стоит отдельно поговорить о том, а как язык управляет временем жизни. Для этого в Python реализовано два механизма:

Механизм сборки мусора.

Каждый объект в Python — это, в первую очередь, объект, унаследованный от базового класса PyObject. Этот самый PyObject содержит всего два поля: ob_refcnt — количество ссылок, и ob_type — указатель на другой объект, тип данного объекта.

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

К сожалению, счетчик ссылок подвержен проблемам в многопоточной среде. Состояния гонки могут приводит к некорректности обновления этого счетчика из разных потоков. Чтобы этого избежать CPython использует GIL — Global Interpreter Lock. Каждый раз, когда происходит работа с памятью, GIL — как глобальная блокировка — препятствует выполнению этих действий одновременно из двух потоков. Он гарантирует, что сначала отработает один, потом другой.

Второй механизм очистки памяти — это сборщик мусора (garbage collector), основанный на идее поколений. Зачем он нам нужен, если есть счетчик ссылок? Дело в том, что счетчик ссылок не позволяет отслеживать ситуации с кольцевыми зависимостями, когда объект A содержит ссылку на B, а B — на A. В таком случае, даже если никто больше не ссылается на объекты A и B, их счетчик всё равно никогда не станет равным нулю и они никогда не удалятся через механизм счетчика ссылок. Для борьбы с такими зависимостями и появился второй механизм (как модуль gc, начиная с версии Python 1.5).

Работает он следующим образом: GC отслеживает объекты (объекты-контейнеры, которые могут содержать ссылки на другие объекты) и смотрит, доступны ли они из основного кода на Python. Если нет, то сборщик их удаляет. Если да — оставляет.

В отличие от счетчика ссылок, механизм сборки мусора не работает постоянно. Он запускается от случая к случаю на основе эвристик. GC разделяет объекты на три поколения. Каждый новый объект начинает свой путь с первого поколения. Если объект переживает раунд сборки мусора, он переходит к более старому поколению. В младших поколениях сборка происходит чаще, чем в старших. Эта практика является стандартной для такого рода сборщиков мусора и снижает частоту и объем очищаемых данных. Идея простая: чем дольше живет объект, тем с большей вероятностью он проживет еще. То есть новые объекты удаляются гораздо чаще, чем те, которые уже просуществовали достаточно долго.

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

Итоги

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

Я хочу внести ясность, я не вижу поведения, описанного в этом вопросе. Вместо этого мой вопрос касается самого вопроса:

Почему не вся память освобождается при выходе из CPython?

И дает такой ответ:

Объекты, на которые ссылаются глобальные пространства имен модулей Python, не всегда освобождаются при выходе из Python. Это может произойти, если есть циклические ссылки. Есть также определенные биты памяти, которые выделяются библиотекой C, которые невозможно освободить (например, такой инструмент, как Purify, будет жаловаться на это). Однако Python агрессивно очищает память при выходе и пытается уничтожить каждый отдельный объект.

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

Это, если предположить, что операционная система с управляемой памятью (Linux, Mac, Windows, GNU, BSD, Solaris . ) звучит как полная чушь.

При выходе из программы (будь то Python или что-то еще) любая запрошенная ею у ОС память освобождается (поскольку ОС контролирует таблицы виртуальных страниц и т. Д. И т. Д.). Программе не нужно ничего выделять или деконструировать (что-то, что раньше должны были делать программы, о чем свидетельствует время, когда чье-то использование cp получило узкое место из-за деконструкции хеш-таблицы), но я не думаю, что какая-либо поддержка Python 3 в ОС ставит это требование к программам.

Имеет ли это смысл в каком-то контексте, о котором я не знаю? К чему это относится?

3 ответа

В FAQ просто говорится, что сам cpython не освобождает активно всю память, которую он получил, когда он завершает работу.

Если вы запускаете cpython на любой обычной серверной / настольной ОС, которая освобождает всю память процесса при выходе, то проблем с утечкой памяти не возникает. ОС заботится об освобождении всей памяти после завершения процесса.

FAQ больше информирует вас о том, что cpython не вызывает free () или аналогичный для всей памяти, выделенной с помощью malloc () или аналогичного. Это может иметь последствия, если вы запускаете cpython в ОС, которая не освобождает всю память, полученную процессом при выходе из процесса (такие операционные системы существуют, в частности, это касается многих встроенных ядер). И если вы запустите cpython под профилировщиком памяти / детектором утечек, этот детектор может сообщить об утечке памяти not free () 'd.

Это может иметь смысл в двух случаях.

Во-первых, запуск анализатора утечки памяти на Python будет сообщать о памяти, которая не была явно освобождена к моменту выхода Python, потому что это указывает на утечку, даже если ОС все это очищает.

Во-вторых, Python предназначен для встраивания в другие программы, и в такой ситуации Python может быть остановлен (и даже перезапущен) без завершения всего процесса. ОС не будет очищать память Python до завершения всего процесса. См. руководство для встраивания Python в другое приложение, а также Документы по Python C API.

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

Я думаю, что в FAQ имеется в виду то, что при выключении процесса python можно оказаться в состояниях, когда память все еще выделяется для процесса, не может быть аккуратно собрана и освобождена от мусора. Это может произойти (как упоминается в FAQ), если у вас есть циклические ссылки и т. Д.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Читайте также: