Очень быстрый ответ : память освобождается, rss
не очень точный инструмент для определения , где используется память , rss
дает меру память, в которой процесс использовал , а не память, в которой процесс использует (продолжайте чтение, чтобы увидеть демонстрацию), вы можете использовать пакет memory-profiler для строка за строкой проверяет использование памяти вашей функцией.
Итак, как заставить модели Django быть освобожденными из памяти? Вы не можете сказать, есть такая проблема, просто используя process.memory_info().rss
.
Однако я могу предложить вам решение для оптимизации вашего кода. И напишите демо о том, почему process.memory_info().rss
не очень точный инструмент для измерения памяти , используемой в каком-то блоке кода.
Предлагаемое решение : как показано ниже в этом же посте, применение del
к списку не будет решением, оптимизация с использованием chunk_size
для iterator
поможет (знайте, * Опция 1028 * для iterator
была добавлена в Django 2.0), это точно, но настоящий враг здесь - этот противный список.
Сказал, что вы можете использовать список только полей, которые вам нужны для выполнения вашего анализа (я предполагаю, что ваш анализ не может быть обработан одним зданием за раз), чтобы уменьшить объем данных, хранящихся в этом списке .
Попробуйте получить необходимые атрибуты на ходу и выберите целевые здания, используя ORM Джанго.
for zip in zips.iterator(): # Using chunk_size here if you're working with Django >= 2.0 might help.
important_buildings = Building.objects.filter(
boundary__within=zip.boundary,
# Some conditions here ...
# You could even use annotations with conditional expressions
# as Case and When.
# Also Q and F expressions.
# It is very uncommon the use case you cannot address
# with Django's ORM.
# Ultimately you could use raw SQL. Anything to avoid having
# a list with the whole object.
)
# And then just load into the list the data you need
# to perform your analysis.
# Analysis according size.
data = important_buildings.values_list('size', flat=True)
# Analysis according height.
data = important_buildings.values_list('height', flat=True)
# Perhaps you need more than one attribute ...
# Analysis according to height and size.
data = important_buildings.values_list('height', 'size')
# Etc ...
Очень важно отметить, что если вы используете такое решение, вы будете попадать в базу данных только при заполнении переменной data
. И, конечно же, в вашей памяти будет только тот минимум, который необходим для выполнения анализа.
Заранее думая.
Когда вы сталкиваетесь с такими проблемами, вы должны начать думать о параллелизме, кластеризации, больших данных и т. Д. ... Читайте также о ElasticSearch , у него очень хорошие возможности анализа.
Демо
process.memory_info().rss
Не буду рассказывать об освобождении памяти.
Я был действительно заинтригован вашим вопросом и тем фактом, который вы описали здесь:
Похоже, список важных_строений перегружает память даже после выхода из области видимости.
Действительно, кажется, но это не так. Посмотрите следующий пример:
from psutil import Process
def memory_test():
a = []
for i in range(10000):
a.append(i)
del a
print(process.memory_info().rss) # Prints 29728768
memory_test()
print(process.memory_info().rss) # Prints 30023680
Так что даже если a
память освобождена, последний номер больше. Это связано с тем, что memory_info.rss()
- это общая память, используемая процессом , а не память , использующая на данный момент, как указано здесь в документации: memory_info .
На следующем рисунке показан график (память / время) для того же кода, что и раньше, но с range(10000000)
Я использую скрипт mprof
, который поставляется в memory-profiler для генерации этого графика.
Вы видите, что память полностью освобождена, это не то, что вы видите при профилировании с использованием process.memory_info().rss
Если я заменим Important_buildings.append (сборка) на _ = сборка, используйте меньше памяти
Так будет всегда, список объектов всегда будет использовать больше памяти, чем один объект.
И с другой стороны, вы также можете видеть, что используемая память не растет линейно, как вы ожидаете. Почему?
С этого превосходного сайта мы можем прочитать:
Метод добавления «амортизируется» O (1). В большинстве случаев память, необходимая для добавления нового значения, уже выделена, что строго равно O (1). После того, как массив C, лежащий в основе списка, был исчерпан, он должен быть расширен, чтобы вместить дальнейшие добавления. Этот периодический процесс расширения является линейным по отношению к размеру нового массива, что, кажется, противоречит нашему утверждению, что добавление - это O (1).
Однако, скорость расширения выбирается умно, чтобы она была в три раза больше предыдущего размера массива ; Когда мы распределяем стоимость расширения по каждому дополнительному приложению, предоставленному этим дополнительным пространством, стоимость за добавление составляет O (1) на амортизированной основе.
Это быстро, но имеет стоимость памяти.
Настоящая проблема не в моделях Django, которые не высвобождаются из памяти . Проблема в том, что алгоритм / решение, которое вы реализовали, использует слишком много памяти. И, конечно же, список злодеев.
Золотое правило для оптимизации Django: замените использование списка для квестов, где только можете.