【问题标题】:Heroku web dyno running Django app not releasing memoryHeroku web dyno 运行 Django 应用程序不释放内存
【发布时间】:2017-04-04 14:40:13
【问题描述】:

在运行 Django 应用程序(使用 gunicorn)时,我在 Heroku 上遇到内存问题。

我有以下代码,它获取用户上传的图像,删除所有 EXIF 数据,并返回准备好上传到 S3 的图像。这既可用作表单数据清理器,也可用于将 base64 数据读入内存。

def sanitise_image(img): # img is InMemoryUploadedFile
    try:
       image = Image.open(img)
    except IOError:
       return None
    # Move all pixel data into a new PIL image
    data = list(image.getdata())
    image_without_exif = Image.new(image.mode, image.size)
    image_without_exif.putdata(data)

    # Create new file with image_without_exif instead of input image.
    thumb_io = StringIO.StringIO()
    image_without_exif.save(thumb_io, format=image.format)
    io_len = thumb_io.len
    thumb_file = InMemoryUploadedFile(thumb_io, None, strip_tags(img.name), img.content_type,
                                  io_len, None)
    # DEL AND CLOSE EVERYTHING
    thumb_file.seek(0)
    img.close()
    del img
    thumb_io.close()
    image_without_exif.close()
    del image_without_exif
    image.close()
    del image
    return thumb_file

我基本上采用 InMemoryUploadedFile 并返回一个仅包含像素数据的新文件。

delcloses 可能是多余的,但它们代表了我尝试修复 Heroku 内存使用量不断增长并且每次此函数终止时都不会释放的情况,即使是过夜:

使用 Guppy 在 localhost 上运行它并按照教程进行操作,堆中没有剩余的 InMemoryUploadedFiles 或 StringIOs 或 PIL Image,这让我感到困惑。

我怀疑 Python 不会将内存释放回操作系统,因为我在 SO 上的多个线程中读过。有没有人玩过InMemoryUploadedFile,可以给我解释一下为什么这个内存没有被释放?

如果我不执行此清理,则不会出现此问题。

非常感谢!

【问题讨论】:

    标签: python django memory heroku


    【解决方案1】:

    我认为问题在于创建临时列表对象:

    data = list(image.getdata())
    

    试试:

    image_without_exif.putdata(image.getdata())
    


    这就是为什么我认为这是问题所在:

    >>> images = [Image.new('RGBA', (100, 100)) for _ in range(100)]
    

    Python 内存使用量增加了 ~4Mb。

    >>> get_datas = [image.getdata() for image in images]
    

    没有增加内存。

    >>> pixel_lists = [list(image.getdata()) for image in images]
    

    Python 内存使用量增加了 ~85Mb。


    除非您明确需要数字,否则您可能不希望将 getdata() 放入列表中。来自枕头docs

    注意,该方法返回的序列对象是内部PIL数据类型,只支持某些序列操作。要将其转换为普通序列(例如用于打印),请使用 list(im.getdata())。

    【讨论】:

    • 嘿!谢谢你的帮助。不幸的是,putdata 需要一个 RGB 元组列表:This method copies data from a sequence object into the image, starting at the upper left corner (0, 0), and continuing until either the image or the sequence ends 我尝试了您所说的确切行,但 putdata 不能接受那种对象,还有 putdata([image.getdata()]) 但无济于事。
    【解决方案2】:

    我最终找到了自己的答案。非常感谢 Ryan Tran 为我指明了正确的方向。 list() 确实会导致泄漏。

    使用等效的 split()merge() 方法 (docs) 这是更新后的代码:

     with Image.open(img) as image:
            comp = image.split()
            image_without_exif = Image.merge(image.mode, comp)
            thumb_io = StringIO.StringIO()
            image_without_exif.save(thumb_io, format=image.format)
            io_len = thumb_io.len
            clean_img = InMemoryUploadedFile(thumb_io, None, strip_tags(img.name), img.content_type,
                                             io_len, None)
            clean_img.seek(0)
     return clean_img
    

    【讨论】:

      猜你喜欢
      • 2013-10-14
      • 2016-03-30
      • 1970-01-01
      • 1970-01-01
      • 2010-09-26
      • 1970-01-01
      • 1970-01-01
      • 2017-11-02
      • 1970-01-01
      相关资源
      最近更新 更多