У меня есть несколько связанных вопросов относительно использования памяти в следующем примере.
Если я запускаю интерпретатор,
foo = ['bar' for _ in xrange(10000000)]
реальная память, используемая на моей машине, достигает
80.9mb
. Затем я,del foo
реальная память уходит, но только на
30.4mb
. Интерпретатор использует4.4mb
базовые показатели, так в чем преимущество отказа26mb
от выделения памяти ОС? Это потому, что Python «планирует наперед», думая, что вы можете снова использовать столько памяти?Почему именно он выпускается
50.5mb
- на основании какой суммы выпускается?Есть ли способ заставить Python освободить всю использованную память (если вы знаете, что больше не будете использовать столько памяти)?
ПРИМЕЧАНИЕ.
Этот вопрос отличается от вопроса Как я могу явно освободить память в Python?
потому что этот вопрос в первую очередь касается увеличения использования памяти по сравнению с базовым уровнем даже после того, как интерпретатор освободил объекты с помощью сборки мусора (с использованием gc.collect
или без).
источник
gc.collect
.Ответы:
Память, выделенная в куче, может подвергаться критическим отметкам. Это усложняется внутренней оптимизацией Python для выделения небольших объектов (
PyObject_Malloc
) в 4 пулах КиБ, классифицированных по размерам выделения, кратным 8 байтам - до 256 байтов (512 байтов в 3.3). Сами пулы находятся на аренах 256 КиБ, поэтому, если используется только один блок в одном пуле, вся арена 256 КБ не будет выпущена. В Python 3.3 распределитель небольших объектов был переключен на использование анонимных карт памяти вместо кучи, поэтому он должен лучше работать при освобождении памяти.Кроме того, встроенные типы поддерживают свободные списки ранее выделенных объектов, которые могут или не могут использовать распределитель малых объектов.
int
Тип поддерживает FreeList со своей собственной выделенной памятью, и ее очистка требует вызовPyInt_ClearFreeList()
. Это можно вызвать косвенно, выполнив полныйgc.collect
.Попробуйте вот так и скажите, что у вас получится. Вот ссылка на psutil.Process.memory_info .
Вывод:
Редактировать:
Я переключился на измерение размера виртуальной машины процесса, чтобы исключить влияние других процессов в системе.
Среда выполнения C (например, glibc, msvcrt) сжимает кучу, когда непрерывное свободное пространство наверху достигает постоянного, динамического или настраиваемого порога. В glibc вы можете настроить это с помощью
mallopt
(M_TRIM_THRESHOLD). Учитывая это, неудивительно, что куча сжимается больше, даже намного больше, чем размер блока, который вы используетеfree
.В 3.x
range
не создается список, поэтому приведенный выше тест не создаст 10 миллионовint
объектов. Даже если бы это было так,int
тип в 3.x по сути является 2.xlong
, который не реализует freelist.источник
memory_info()
вместоget_memory_info()
иx
int
с даже в Python 3, но каждая из них заменяет последнюю в переменной цикла, поэтому они не все существуют одновременно.Я предполагаю, что вас действительно волнует вопрос:
Нет, нет. Но есть простой обходной путь: дочерние процессы.
Если вам нужно 500 МБ временного хранилища на 5 минут, но после этого вам нужно поработать еще 2 часа и больше никогда не трогать столько памяти, создайте дочерний процесс для выполнения работы с интенсивным использованием памяти. Когда дочерний процесс уходит, память освобождается.
Это не совсем тривиально и бесплатно, но это довольно просто и дешево, что обычно достаточно для того, чтобы торговля окупалась.
Во-первых, самый простой способ создать дочерний процесс - использовать
concurrent.futures
(или, для версии 3.1 и ранее,futures
backport на PyPI):Если вам нужно немного больше контроля, используйте
multiprocessing
модуль.Затраты:
mmap
ped или иначе; API разделяемой памяти вmultiprocessing
и т. д.).struct
доступными или, в идеале, -ctypes
доступными).источник
eryksun ответил на вопрос №1, а я ответил на вопрос №3 (исходный №4), а теперь давайте ответим на вопрос №2:
В конечном итоге он основан на целом ряде совпадений внутри Python
malloc
, которые очень трудно предсказать.Во-первых, в зависимости от того, как вы измеряете память, вы можете измерять только страницы, фактически отображенные в памяти. В этом случае каждый раз, когда пейджер выгружает страницу, память будет отображаться как «освобожденная», даже если она не была освобождена.
Или вы можете измерять используемые страницы, которые могут или не могут подсчитывать выделенные, но никогда не задействованные страницы (в системах, которые оптимистично выделяют избыточное количество, например Linux), страницы, которые выделены, но помечены
MADV_FREE
, и т. Д.Если вы действительно измеряете выделенные страницы (что на самом деле не очень полезно делать, но, похоже, именно об этом вы спрашиваете), и страницы действительно были освобождены, это может произойти в двух случаях: «Либо вы» ve использовал
brk
или аналогичный для сжатия сегмента данных (в настоящее время очень редко), или вы использовалиmunmap
или подобное для освобождения сопоставленного сегмента. (Теоретически также существует незначительный вариант последнего, поскольку есть способы освободить часть сопоставленного сегмента - например, украсть егоMAP_FIXED
дляMADV_FREE
сегмента, который вы немедленно отключаете.)Но большинство программ не выделяют данные напрямую из страниц памяти; они используют
malloc
распределитель в стиле. Когда вы вызываетеfree
, распределитель может только освободить страницы для ОС, если вы случайно оказалисьfree
последним живым объектом в отображении (или на последних N страницах сегмента данных). Ваше приложение не может разумно предсказать это или даже заранее определить, что это произошло.CPython делает это еще более сложным - он имеет настраиваемый двухуровневый распределитель объектов поверх настраиваемого распределителя памяти
malloc
. (См. Комментарии к источнику для более подробного объяснения.) Кроме того, даже на уровне C API, не говоря уже о Python, вы даже не контролируете напрямую, когда освобождаются объекты верхнего уровня.Итак, когда вы освобождаете объект, как узнать, освобождает ли он память для ОС? Что ж, сначала вы должны знать, что выпустили последнюю ссылку (включая все внутренние ссылки, о которых вы не знали), позволяя GC освободить ее. (В отличие от других реализаций, по крайней мере, CPython освободит объект, как только это будет разрешено.) Обычно это освобождает как минимум две вещи на следующем уровне ниже (например, для строки вы освобождаете
PyString
объект, а строковый буфер ).Если вы действительно освобождаете объект, чтобы узнать, приведет ли это к освобождению блока хранилища объектов на следующем уровне, вы должны знать внутреннее состояние распределителя объектов, а также то, как оно реализовано. (Очевидно, что этого не произойдет, если вы не освободите последнее место в блоке, и даже тогда этого может не произойти.)
Если вы делаете освободить блок хранения объекта, чтобы узнать , вызывает ли это
free
вызов, вы должны знать внутреннее состояние распределителя PyMem, а также , как это реализовано. (Опять же, вы должны освободить последний использованный блок вmalloc
редактируемой области, и даже тогда этого может не произойти.)Если вы делаете
free
вmalloc
области Е.Д., чтобы узнать , вызывает ли ли этоmunmap
или эквивалент (илиbrk
), вы должны знать внутреннее состояниеmalloc
, а также , как это реализовано. И этот, в отличие от других, сильно зависит от платформы. (И опять же , вы вообще должны быть deallocating последних в использованииmalloc
внутриmmap
сегмента, и даже тогда, это не может произойти.)Итак, если вы хотите понять, почему было выпущено ровно 50,5 МБ, вам придется отследить его снизу вверх. Почему
malloc
при выполнении одного или несколькихfree
вызовов было отключено отображение страниц объемом 50,5 МБ (вероятно, это немного больше 50,5 МБ)? Вам нужно будет прочитать свою платформуmalloc
, а затем просмотреть различные таблицы и списки, чтобы увидеть ее текущее состояние. (На некоторых платформах он может даже использовать информацию системного уровня, которую практически невозможно захватить без создания снимка системы для проверки в автономном режиме, но, к счастью, обычно это не проблема.) И тогда вам придется сделайте то же самое на трех уровнях выше.Итак, единственный полезный ответ на вопрос - «Потому что».
Если вы не занимаетесь разработкой с ограниченными ресурсами (например, встроенной), у вас нет причин заботиться об этих деталях.
А если будут делать ресурсы ограниченного развития, зная эти детали бесполезно; вам в значительной степени необходимо выполнить конечный прогон на всех этих уровнях и, в частности,
mmap
на памяти, которая вам нужна на уровне приложения (возможно, с одним простым, хорошо понятным распределителем зон для конкретного приложения между ними).источник
Во-первых, вы можете установить взгляды:
Тогда запускайте его в терминале!
В коде Python добавьте в начало файла следующее:
После использования переменной «Big» (например, myBigVar), для которой вы хотите освободить память, напишите в коде Python следующее:
В другом терминале запустите свой код python и наблюдайте в терминале "взглядов", как управляется память в вашей системе!
Удачи!
PS Я предполагаю, что вы работаете в системе Debian или Ubuntu
источник