LINUX.ORG.RU

python помогите фиксануть утечку

 , ,


1

2

Ребят есть кусок рабочего кода, я его упростил до предела, после работы жрёт 3 гига памяти, и через gc.collect() не хочет её освобождать.

Может кто-нибудь пожалуйста исправить утечку?

В функции engine я раздуваю объект, и всё труба, вернуть память назад уже никак не получается.

Код:

from concurrent.futures import ProcessPoolExecutor
import time
import gc
import pdb
from queue import Queue

# через сколько выполнений обнулять пул
POOL_RESET_PERIOD = 10
# сколько заданий закидывать в пул
POOL_TASK_CHUNK = 10

q = Queue()


# класс в котором будет тяжёлый объект
class MyHugeTask:
    def __init__(self):
        self.cont = {}


# функция обработчик
def engine(box):
    # раздуваем объект чтобы он начал жрать память
    box.cont["123"] = "123"*1000*100*100*10
    return box

# закидываем таски для теста
for i in range(50):
    q.put(MyHugeTask())


def main_run(queue):
    print("принято заданий: {}".format(queue.qsize()))
    pool = None
    count = 0
    it = 0

    while True:
        it += 1
        # reset pool через каждые N выполнений
        if pool is None or not count % POOL_RESET_PERIOD:
            if pool is not None:
                pool.shutdown()
            pool = ProcessPoolExecutor(max_workers=1)
            print('Pool reseted')

        if queue.qsize() is 0:
            break

        # добавляем задания в пул
        futs = []
        for x in range(POOL_TASK_CHUNK):
            if queue.qsize() is 0:
                break
            # увеличиваем счётчик пулла
            count += 1
            # пробуем взять задание
            try:
                task = queue.get()
            except:
                break
            else:
                # задание в пул
                fut = pool.submit(engine, task)
                del task
                # для отчёта
                futs.append(fut)
        print('Submited {} tasks into pool'.format(len(futs)))

        # смотрим result'ы работы пула
        for fut in futs:
            try:
                box = fut.result()
            except Exception as ex:
                print('exception: {}'.format(ex))
            else:
                # удаляем использованный таск чтобы очистить память
                del box
                wow = 1
        del futs
        print("круг: {} finished".format(it))

    # ждём когда пулл всё выполнит
    while True:
        if len(pool._pending_work_items) is not 0:
            time.sleep(1)
        else:
            break
    print("pool finished")
    print("попробуйте теперь собрать мусор через gc.collect() и память не высвободится")
    pdb.set_trace()

if __name__ == '__main__':
    main_run(q)



Последнее исправление: rubro (всего исправлений: 1)

Ну и макароны.

после работы жрёт 3 гига памяти, и через gc.collect() не хочет её освобождать

После завершения работы скрипта не хочет память освобожать?

Siado ★★★★★
()
Ответ на: комментарий от Siado

Извиняюсь я начинающий, мне очень трудно упростить ещё сильнее. Там идёт вызов отладчика pdb.set_trace() и вот на этом уровне я не могу очистить память, она высвободится только когда я выйду вообще из функции main_run()

rubro
() автор топика

Боже святый бессмертный. Ладно код страшный и, по-моему, некорректный, но синхронизация через time.sleep... доступ к приватным полям стандартного класса...

Ты уверен, что поле _pending_work_items действительно освобождается не раньше, чем все задачи будут выполнены?

proud_anon ★★★★★
()
Ответ на: комментарий от Siado

Спасибо изучаю, правда мне не понятно как там отлавливать ошибки, при первой же ошибке тот пример валится

rubro
() автор топика
Ответ на: комментарий от proud_anon

Спасибо за критические замечания!

Код я нашёл в интернете и немного переделал под себя, к сожалению на ваши вопросы ответить мне не хватает квалификации.

Могу сказать только что код работает прекрасно, и единственное что мне в нём не устраивает - это утечка памяти.

rubro
() автор топика
Ответ на: комментарий от rubro

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

portquest2016
()
Ответ на: комментарий от portquest2016
import os

def print_rss():
    status_file = '/proc/%i/status' % os.getpid()
    rss_line = open(status_file).readlines()[21]
    print(rss_line, end='')

print_rss()

a = "123"*1000*100*100*10
print_rss()

a = None
print_rss()
$ python mem.py
VmRSS:	    7340 kB
VmRSS:	  300488 kB
VmRSS:	    7516 kB
anonymous
()
Ответ на: комментарий от rubro

Да. Нужно остановить пул когда он больше не нужен. Есть вероятность что он держит ссылки на кое какие futures

redixin ★★★★
()

А еще тебе не обязательно возвращать то что получил в engine. Это тоже пофиксает «утечку»

redixin ★★★★
()
Ответ на: комментарий от redixin

Так там уже есть код

        # reset pool через каждые N выполнений
        if pool is None or not count % POOL_RESET_PERIOD:
            if pool is not None:
                pool.shutdown()

rubro
() автор топика
Ответ на: комментарий от redixin

Спасибо за советы!

Дело в том, что тот объект который возвращается - мне надо с ним ещё провести ряд манипуляций именно на этом уровне. Зачем - чтобы собрать результаты работы.

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

rubro
() автор топика

Закрыл я эту утечку, как - сам вообще ничего не понял, поменял del'ы на установку None, ещё пару мест поменял, в общем магия но всё заработало и теперь не течёт, делаю чистку gc.collect() после каждого захода пула и всё хорошо.

Всем спасибо за участие, ваши советы я внимательно читаю и принимаю к сведению, критика ваша тоже очень полезна.

rubro
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.