Python: сборка мусора не удается? - PullRequest
4 голосов
/ 08 марта 2012

Рассмотрим следующий сценарий:

l = [i for i in range(int(1e8))]
l = []
import gc
gc.collect()
# 0
gc.get_referrers(l)
# [{'__builtins__': <module '__builtin__' (built-in)>, 'l': [], '__package__': None, 'i': 99999999, 'gc': <module 'gc' (built-in)>, '__name__': '__main__', '__doc__': None}]
del l
gc.collect()
# 0

Дело в том, что после всех этих шагов использование памяти этим процессом python составляет около 30% на моем компьютере (Python 2.6.5, более подробная информация по запросу?).Вот выдержка из вывода top:

 PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND  
5478 moooeeeep 20   0 2397m 2.3g 3428 S    0 29.8   0:09.15 ipython  

соотв.ps aux:

moooeeeep 5478  1.0 29.7 2454720 2413516 pts/2 S+   12:39   0:09 /usr/bin/python /usr/bin/ipython gctest.py

Согласно документам для gc.collect:

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

Означает ли это, что если мне (временно) нужно большое количество различных int или float чисел, мне нужно экспортироватьэто на C / C ++, потому что Python GC не может освободить память?


Обновление

Вероятно, виноват интерпретатор, как эта статья предлагает:

Вы создали одновременно 5 миллионов целых чисел, и каждый объект int потребляет 12 байтов.«Для скорости», Python поддерживает внутренний свободный список для целочисленных объектов.К сожалению, этот бесплатный список бессмертен и неограничен по размеру.float также использует бессмертный и неограниченный свободный список.

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


Обновление 2

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

Ответы [ 4 ]

7 голосов
/ 08 марта 2012

Я провел несколько тестов, и эта проблема возникает только в CPython 2.x.Эта проблема устранена в CPython 3.2.2 (он возвращается к использованию памяти новым интерпретатором), а PyPy 1.8 (python 2.7.2) также возвращается к тому же уровню, что и новый процесс pypy.

Так что нет, вам не нужно переключаться на другой язык.Однако, вероятно, есть решение, которое не заставит вас переключиться на другую реализацию Python.

7 голосов
/ 08 марта 2012

Ваш ответ может быть здесь :

Python выполняет много распределений и освобождений.Все объекты, включая «простые» типы, такие как целые числа и числа с плавающей точкой, хранятся в куче.Вызов malloc и free для каждой переменной будет очень медленным.Следовательно, интерпретатор Python использует различные оптимизированные схемы распределения памяти.Наиболее важной из них является реализация malloc, называемая pymalloc, специально разработанная для обработки большого количества небольших выделений.Любой объект, который меньше 256 байт, использует этот распределитель, в то время как все, что больше, использует системный malloc. Эта реализация никогда не возвращает память операционной системе.Вместо этого он удерживает его на тот случай, если он понадобится снова .Это эффективно при повторном использовании в течение короткого времени, но неэффективно, если проходит много времени, прежде чем оно потребуется.

6 голосов
/ 08 марта 2012

На это также ответит Алекс Мартелли в другой теме .

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

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

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

К счастью, мне удалось разделить интенсивную память на отдельные части, которые позволили интерпретатору фактически освободить временную память после каждой итерации. Я использовал следующую оболочку для запуска функции интенсивного использования памяти в качестве подпроцесса:

import multiprocessing

def run_as_process(func, *args):
    p = multiprocessing.Process(target=func, args=args)
    try:
        p.start()
        p.join()
    finally:
        p.terminate()
0 голосов
/ 08 марта 2012

Python имеет тенденцию собирать мусор довольно разумно, и, по моему опыту, освобождает память просто отлично. Он имеет небольшие накладные расходы (около 15 МБ на моем компьютере), но помимо этого требования к памяти не сильно отличаются от C. Если вы имеете дело с таким большим количеством данных, что память является серьезной проблемой, вы, вероятно, собираетесь У меня такая же проблема в C, так что было бы гораздо лучше попытаться изменить способ работы с вашими данными, например, сохранить их в файле подкачки и работать с управляемыми блоками по одному.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...