eryksun ответил на вопрос №1, а я ответил на вопрос №3 (исходный №4), а теперь давайте ответим на вопрос №2:
Почему именно он выпускает 50,5 МБ - исходя из какой суммы выделяется?
В конечном итоге он основан на целом ряде совпадений внутри 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
на памяти, которая вам нужна на уровне приложения (возможно, с одним простым, хорошо понятным распределителем зон для конкретного приложения между ними).