LINUX.ORG.RU

python освобождение памяти

 ,


1

3

Привет, ребята. Я пишу на django проектик, и там есть работа с фотками. Сохранить как jpg, сделать тумбочку. Работа с пиксами происходит с помощью pil.

Так вот, когда pil попадается большая картинка, то он выделяет много памяти для неё, python эту память не освобождает (в ожидании следующего раза). И в результате после этого инстанс джанги, которому выпало обрабатывать картинку остается с этой выделенной памятью. В нормальном состоянии инстанс джанги потребляет 33 МБ, после обработки картинки потребление возрастает пропорционально размеру картинки. Например фотка 7.7MP заставляет python уже жрать 65 МБ памяти.

Так вот вопрос, оно отпустит когда-нибудь эту память или нет? Отпустит оно её просто со временем или отпустит оно её когда она будет нужна другому процессу?


Если правильно все делать, то должен сразу высвобождать. Про гц ман почитать тоже будет не лишним, но это не твой случай.

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

И что я с этим должен делать? Я знаю про garbage collection и перекрестные ссылки. У меня нет второго чтобы оно могло мешать первому. Эта ситуация проявляется на совершенно тупом примере, где вся работа с изображением происходит в одном методе класса модели, и все что было в нем должно безвозбранно уничтожиться после выхода. Меня интересует как поведут себя эти распухшие инстансы на практике.

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

На практике если нет циклов и не отдает сразу, то не отдаст никогда.

redixin ★★★★
()

Ты так говоришь, как будто у тебя идеальный код и проблема в интерпретаторе как таковом.

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

Эта проблема проявляется в простейшей ситуации, там просто нельзя что-то сделать неправильно.

Вот сама модель где всё и происходит:

from django.db import models
from PIL import Image


class Photo(models.Model):
    image = models.ImageField()

    def save(self, *args, **kwargs):
        print('save called')
        image = Image.open(self.image.file)
        print(image.size)
        image.save('/tmp/lol123.jpg', 'JPEG')
        del(image)

Ну а это простая регистрация модели в админке чтобы было где тыркать:

from django.contrib import admin
from .models import Photo

# Register your models here.
class PhotoAdmin(admin.ModelAdmin):
    model = Photo
    list_per_page = 5

admin.site.register(Photo, PhotoAdmin)

Более того, в интерактивном режиме все работает как надо, после del потребление памяти падает на 30МБ.

Python 3.4.2 (default, Oct  8 2014, 10:45:20) 
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from PIL import Image
>>> image = Image.open('/home/roma/test.jpg')
>>> image.save('/tmp/img.jpg', 'JPEG')
>>> del(image)
>>> 

Ну и тут напрашивается вывод, что питон предусматривает следующие инструкции и на основе этого решает нужно или нет освободить память. Ну и опять же изначальный вопрос - какой итог? Я получу постоянно жирные инстансы джанги под 100МБ, которые сделают её даже хуже чем cgi или оно как-то освободит эту память?

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

Да ничего он на перед не думает. Тем более PIL. Тут больше похоже что память кто то другой съел. Пробовал то же самое без PIL?

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

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

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

В-третьих сейчас и так скажут.

Лол, кукаретики против принтов.

A1
()
Ответ на: комментарий от PoMbl4

Ну и опять же изначальный вопрос - какой итог?

Без джанги не ест, с джангой ест. Сам то как думаешь?

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

Я привел выше максимально простой пример, там кроме работы с pil больше ничего нет. Джанго-проект был создан пустым, было создано приложение в нём, и в приложении были написаны эти несколько строк. Исключи его - вообще ничего не останется. Вообще если не добавлять новые фотки оно работает в пределах 30-40 МБ неделями при 50-100 посетителях в день.

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

Попробуй pil image присобачить к инстансу класса этой фотки. В класс методе перепиши self.image_file = Image.open()

И удаляй уже в этом методе через del self.image_file

Также было бы неплохо через профайлеры посмотреть - удаляется ли инстанс этой фотки из джанги после использования / удаляй вручную на крайняк. Попробуй в иных случаях другие orm типа pony orm.

menangen ★★★★★
()

заставляет python уже жрать 65 МБ памяти

А как ты это определяешь? Пистон не отдает память системе после освобождения больших объектов, она просто остается лежать в куче для последующего использования.

no-such-file ★★★★★
()
Ответ на: комментарий от PoMbl4

Вызов del(image) не имеет смысла, всё что он делает - удаляет ссылку с именем image, которая и так после выхода из функции сама пропадет. Лучше вызови image.close(), если такой есть.

ei-grad ★★★★★
()
Последнее исправление: ei-grad (всего исправлений: 1)
Ответ на: комментарий от no-such-file

У нормальных людей нет проблемы подержать лишние 100Мб памяти между запросами. Btw, ты ведь понимаешь что del ссылки не вызывает __del__ объекта?

ei-grad ★★★★★
()
Ответ на: комментарий от no-such-file

Пистон не отдает память системе после освобождения больших объектов, она просто остается лежать в куче для последующего использования.

Ты с каких пор знатоком питона заделался?

>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
10668
0
>>> a = list(range(10000000))
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
407980
0
>>> del a
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
10692
0

A1
()
Ответ на: комментарий от ei-grad

Btw, ты ведь понимаешь что del ссылки не вызывает __del__ объекта?

Ого, а можно для троечников поподробнее? Я вот думал, что del последней ссылки вызывает __del__ объекта.

t184256 ★★★★★
()

Сто сохранений с цикле сделай и посмотри что будет с памятью

JN
()
Ответ на: комментарий от no-such-file

Да. Но чем

del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and the latter is only called when x‘s reference count reaches zero.

эффективно отличается от

del последней ссылки вызывает __del__ объекта

? А то я че-то занервничал.

t184256 ★★★★★
()
Последнее исправление: t184256 (всего исправлений: 1)
Ответ на: комментарий от A1

Это третий питон. А вот второй:

>>> import os
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
 7060
0
>>> a=range(10000000)
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
325436
0
>>> del a
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
247308
0

Ты с каких пор знатоком питона заделался?

Ошибка молодости, каюсь теперь.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от anonymous

И кстати, может питон правильно делает, что не дает тебе тасовать память туда-сюда при каждом запросе. Каких тормозов это добавит мы же не знаем. А если такие запросы раз в секунду повалят?

anonymous
()
Ответ на: комментарий от no-such-file

А вот второй

~$ python2
Python 2.7.11 (default, Dec  6 2015, 15:43:46) 
[GCC 5.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Persistent session history and tab completion are enabled.
>>> import array
>>> a = array.array('i', xrange(100000000))
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
399356
0
>>> del a
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
 8136
0

Вообще это конечно не совсем честно, но память, выделенная в с-расширениях (PIL), должна прекрасно возвращаться.

A1
()
Ответ на: комментарий от ei-grad

Для троечников в мануале написано - вызывайте close явно.

Это-то понятно

А del и gc это материал для ударников.

А это — непонятно.

t184256 ★★★★★
()

Так вот вопрос, оно отпустит когда-нибудь эту память или нет?

Зависит от ОС, версии питона, а также как он был скомпилирован и как написан модуль (на чистом питоне или сишным модулем).

Отпустит оно её просто со временем или отпустит оно её когда она будет нужна другому процессу?

В GNU/Linux после free(), помеченная как освобожденная память будет отдана другому процессу при явной необходимости. Однако, следующий вызов в твоей программе может получить участок из swap, и соответствующие последствия. Так что думай, как лучше проектировать программу и когда освобождать память. В большинстве случаев тебе нужно заботиться, чтобы хватало ОЗУ с запасом и не полагаться на swap или что ОС сделает все за тебя.

Некоторые особенности описаны тут: http://stackoverflow.com/questions/15455048/releasing-memory-in-python

+ https://docs.python.org/3/library/tracemalloc.html

P.S. Не спец по питону.

gh0stwizard ★★★★★
()
Последнее исправление: gh0stwizard (всего исправлений: 2)
Ответ на: комментарий от A1

array.array('i', xrange(100000000))

Ты бы ещё mmap принёс.

но память, выделенная в с-расширениях (PIL), должна прекрасно возвращаться

Должна, но очевидно не возвращается. Все вопросы к авторам PIL.

no-such-file ★★★★★
()
Ответ на: комментарий от ei-grad

Единственный адекватный ответ в треде.

Deleted
()
Ответ на: комментарий от reprimand

сделать тумбочку

это как?!

Как бочку, только квадратную и с дверкой.

no-such-file ★★★★★
()
Ответ на: комментарий от A1

дебаг включен и все ссылки на файло продолжают висеть

Возможно.

Потыкал во втором пистоне:

Python 2.7.10 (default, Nov 21 2015, 13:55:54) 
[GCC 4.9.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> import PIL
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
 7272
0
>>> img = Image.open('/tmp/pic.jpeg')
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
11356
0
>>> del img
>>> os.system('ps -o rss {}'.format(os.getpid()))
  RSS
11380
0

ЧТД. Надеюсь, ТС разберется, где он там чего провтыкал.

no-such-file ★★★★★
()

Там рандом какой-то. То освобождает, то нет. Код и картинка одни и те же.

Это тестовый скрипт.

from PIL import Image
import os

def test(path):
    image = Image.open(path)
    image.save('/tmp/img.jpg', 'JPEG')
    image.close()
    del(image)
    image = None

while True:
    p = input()
    p = p.replace('file://', '')
    print('path ', p)
    test(p)
    os.system('ps aux | grep python')

А это вывод:

roma@debian:~$ python3 1.py 
file:///home/roma/test.jpg
path  /home/roma/test.jpg
roma     22363  4.8  0.6  73752 41280 pts/1    S+   13:27   0:00 python3 1.py
roma     22364  0.0  0.0   4328   860 pts/1    S+   13:27   0:00 sh -c ps aux | grep python
roma     22366  0.0  0.0  13940  2328 pts/1    S+   13:27   0:00 grep python
^CTraceback (most recent call last):
  File "1.py", line 12, in <module>
    p = input()
KeyboardInterrupt
roma@debian:~$ python3 1.py 
file:///home/roma/test.jpg
path  /home/roma/test.jpg
roma     22367  5.7  0.1  43552 11236 pts/1    S+   13:27   0:00 python3 1.py
roma     22368  0.0  0.0   4328   768 pts/1    S+   13:27   0:00 sh -c ps aux | grep python
roma     22370  0.0  0.0  13940  2328 pts/1    S+   13:27   0:00 grep python
file:///home/roma/test.jpg
path  /home/roma/test.jpg
roma     22367  3.2  0.1  43552 11364 pts/1    S+   13:27   0:00 python3 1.py
roma     22371  0.0  0.0   4328   712 pts/1    S+   13:27   0:00 sh -c ps aux | grep python
roma     22373  0.0  0.0  13940  2344 pts/1    S+   13:27   0:00 grep python
file:///home/roma/test.jpg
path  /home/roma/test.jpg
roma     22367  3.8  0.1  43552 11364 pts/1    S+   13:27   0:00 python3 1.py
roma     22374  0.0  0.0   4328   724 pts/1    S+   13:27   0:00 sh -c ps aux | grep python
roma     22376  0.0  0.0  13940  2344 pts/1    S+   13:27   0:00 grep python
file:///home/roma/test.jpg
path  /home/roma/test.jpg
roma     22367  3.9  0.1  43552 11364 pts/1    S+   13:27   0:00 python3 1.py
roma     22377  0.0  0.0   4328   796 pts/1    S+   13:27   0:00 sh -c ps aux | grep python
roma     22379  0.0  0.0  13940  2264 pts/1    S+   13:27   0:00 grep python


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

Переделал все операции с изображениями на обёртку к imagemagick под названием wand. С ней инстанс джанги после обработки кучи хайрезных фоток (169 файлов по 7.7MP) жрет всего 58мб, с pil жрало от 122мб, и более того если его кормить мелкими пикчами, то он имеет небольшую тенденцию к уменьшению потребления памяти. Ну а pil видимо текучее дерьмо, худшая либа которую я когда либо видел, жаль что оно стало стандартом. Привет ребяткам, которые предлагали оставить всё как есть и довольствоваться жирным инстансом.

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

Для библиотеки которая старше тебя она не так уж плоха, да. А вообще народ давно pillow юзает. Но wand тоже неплохо выглядит.

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

Не старше 8) С wand другая проблема, у него после вызова метода transform остаются 3 зомби-потока и их никак не убрать. И более того судя по загрузке цп они там даже что-то делают, уж не биткоины ли? Буду пробовать pgmagick.

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