Хобрук: Ваш путь к мастерству в программировании

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

Код чрезвычайно прост. Утечек быть не должно, так как все делается внутри функции. И ничего не возвращается. У меня есть функция, которая просматривает все строки в файле (~ 20 МБ) и помещает их все в список.
Упомянутая функция:

def read_art_file(filename, path_to_dir):
    import codecs
    corpus = []
    corpus_file = codecs.open(path_to_dir + filename, 'r', 'iso-8859-15')
    newline = corpus_file.readline().strip()
    while newline != '':
        # we put into @article a @newline of file and some other info
        # (i left those lists blank for readability)
        article = [newline, [], [], [], [], [], [], [], [], [], [], [], []]
        corpus.append(article)
        del newline
        del article
        newline = corpus_file.readline().strip()
    memory_usage('inside function')
    for article in corpus:
        for word in article:
            del word
        del article
    del corpus
    corpus_file.close()
    memory_usage('inside: after corp deleted')
    return

Вот основной код:

memory_usage('START')
path_to_dir = '/home/soshial/internship/training_data/parser_output/'
read_art_file('accounting.n.txt.wpr.art', path_to_dir)
memory_usage('outside func')
time.sleep(5)
memory_usage('END')

Все memory_usage просто печатает количество КиБ, выделенное сценарием.

Выполнение скрипта

Если я запускаю скрипт, он дает мне:

НАЧАЛЬНАЯ память: 6088 КБ
внутренняя память: 393752 КБ (файл 20 МБ + списки занимают 400 МБ)
внутренняя: после удаления корпорации память: 43360 КБ
внешняя функциональная память: 34300 КиБ (34300-6088= 28 МиБ утекло)
ФИНИШ памяти: 34300 КиБ

Выполнение без списков

И если я сделаю абсолютно то же самое, но с добавлением article к закомментированному corpus:

article = [newline, [], [], [], [], [], ...]  # we still assign data to `article`
# corpus.append(article)  # we don't have this string during second execution

Таким образом, вывод дает мне:

НАЧАЛЬНАЯ память: 6076 КБ
внутренняя память: 6076 КБ
внутренняя: после удаления корпорации память: 6076 КБ
внешняя функциональная память: 6076 КБ
КОНЕЧНАЯ память: 6076 КиБ

ВОПРОС:

Следовательно, таким образом освобождается вся память. Мне нужно освободить всю память, так как я собираюсь обрабатывать сотни таких файлов.
Я что-то делаю не так или это ошибка интерпретатора CPython?

UPD. Вот как я проверяю потребление памяти (взято из другого вопроса о стеке):

def memory_usage(text = ''):
    """Memory usage of the current process in kilobytes."""
    status = None
    result = {'peak': 0, 'rss': 0}
    try:
        # This will only work on systems with a /proc file system
        # (like Linux).
        status = open('/proc/self/status')
        for line in status:
            parts = line.split()
            key = parts[0][2:-1].lower()
            if key in result:
                result[key] = int(parts[1])
    finally:
        if status is not None:
            status.close()
    print('>', text, 'memory:', result['rss'], 'KiB  ')
    return

  • на самом деле вы не удаляете список корпусов в своем коде? также вы никогда явно не закрываете corpus_file 07.05.2013
  • как вы измеряете потребление памяти? 07.05.2013
  • @CameronSparr, спасибо. Я обновил код и результаты отладки вывода. 07.05.2013
  • @mgilson, я добавил функцию проверки памяти, как вы просили. благодарю вас 07.05.2013
  • Если вы вызовете read_art_file дважды, каково будет сообщение об использовании памяти? Растет ли он (утечка) или остается прежним (согласно ответу/гипотезе Мгилсона ниже)? 07.05.2013
  • Это не утечка памяти. Это артефакт того, как вы измеряете использование памяти. Вы видите объем памяти, который процесс Python удерживает от операционной системы; Python может повторно использовать эту память для разных объектов в течение жизни процесса вместо того, чтобы запрашивать больше памяти у ОС. 07.05.2013

Ответы:


1

Обратите внимание, что python никогда не гарантирует, что любая память, используемая вашим кодом, действительно будет возвращена ОС. Все, что гарантирует сборка мусора, — это то, что память, используемая объектом, который был собран, может быть использована другим объектом в будущем.

Из того, что я читал1 о реализации распределителя памяти в Cpython, для эффективности память распределяется в "пулах". Когда пул заполнен, python выделит новый пул. Если пул содержит только мертвые объекты, Cpython фактически освобождает память, связанную с этим пулом, но в противном случае этого не происходит. Это может привести к тому, что несколько частично заполненных пулов будут зависать после функции или чего-то еще. Однако на самом деле это не означает, что это «утечка памяти». (Cpython все еще знает о памяти и потенциально может освободить ее позже).

1Я не разрабатываю Python, поэтому эти данные могут быть неверными или, по крайней мере, неполными

07.05.2013
  • Также обратите внимание, что память, которая не используется активно, может быть сброшена в кеш, поэтому, даже если Python не возвращает ее ОС, ее все равно можно использовать повторно. 07.05.2013
  • @MarkRansom - Да, это то, что я имел в виду, говоря, что его можно использовать другим объектом в будущем. Если вы знаете больше о деталях здесь, не стесняйтесь редактировать / исправлять любые утверждения выше, которые я мог получить неправильно. 07.05.2013
  • Извините, я не ясно выразился. Я имел в виду, что память может быть полностью использована другой программой без необходимости сначала возвращать ее в ОС. 07.05.2013
  • @MarkRansom -- О ... Интересно. Я ничего об этом не знаю. Аккуратный. 07.05.2013

  • 2

    Эта петля

    for article in corpus:
        for word in article:
            del word
        del article
    

    не освобождает память. del word просто уменьшает счетчик ссылок объекта, на который ссылается имя word. Однако ваш цикл увеличивает счетчик ссылок каждого объекта на единицу, когда установлена ​​переменная цикла. Другими словами, из-за этого цикла нет чистого изменения счетчика ссылок любого объекта.

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

    07.05.2013
  • Но у меня все еще есть линия del corpus. Ты видел это? 07.05.2013
  • Да. И это уменьшает счетчик ссылок на список, на который ссылается corpus, на единицу. Если счетчик ссылок на этот список достигает 0 и нет других ссылок на что-либо, содержащееся в этом списке, тогда все эти элементы могут быть удалены сборщиком мусора. Но если вы закомментируете вызов corpus.append, этот список начнет жизнь как — и останется — пустым списком, в котором нечего собирать мусор. 07.05.2013
  • Да, но мой вопрос: если я сделаю del список в обоих исполнениях, результирующий объем памяти должен быть одинаковым после выхода из функции, не так ли? 07.05.2013
  • Нет. Закомментирование corpus.append уменьшает объем памяти, выделяемой вашей программой, поскольку память, на которую ссылается article, повторно используется во время чтения файла, поскольку в corpus в ходе цикла ничего не сохраняется. 07.05.2013
  • Новые материалы

    HMTL - Многозадачное обучение для решения задач НЛП
    Достижение результатов SOTA путем передачи знаний между задачами Область обработки естественного языка включает в себя десятки задач, среди которых машинный перевод, распознавание именованных..

    Решения DBA Metrix
    DBA Metrix Solutions предоставляет удаленного администратора базы данных (DBA), который несет ответственность за внедрение, обслуживание, настройку, восстановление базы данных, а также другие..

    Начало работы с Блум
    Обзор и Codelab для генерации текста с помощью Bloom Оглавление Что такое Блум? Некоторые предостережения Настройка среды Скачивание предварительно обученного токенизатора и модели..

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

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

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

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