Skip to content

Memory is slowly leaking #2019

Description

@dmpetrov

Memory is slowly leaking when process many image files one by one.

Leak size:
100K images --> ~100MB
300K images --> ~200MB
1M images --> ~1GB

Here you can find reproducing code examples: PyWavelets/pywt#180

Activity

  1. hugovk commented on Jul 8, 2016

    @hugovk
    Member

    @dmpetrov Please can you give a minimal code example reproducing the problem that uses Pillow but no third-party libraries like numpy or pywt? Thanks!

  2. dmpetrov commented on Jul 9, 2016

    @dmpetrov
    Author
    import os
    import PIL
    from PIL import Image
    
    dir = "/Volumes/Seagate/storage/kaggle/avito/images"
    onlyfiles = []
    dirs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
    for d in dirs:
        filedir = os.path.join(dir, str(d))
        files = [f for f in os.listdir(filedir) if os.path.isfile(os.path.join(filedir, f))]
        filepaths = map( lambda x: os.path.join(filedir, x), files )
        onlyfiles += list(filepaths)
    
    s = 0
    for i in range(len(onlyfiles)):
        fname = onlyfiles[i]
        image = PIL.Image.open(fname)
        # Do something
        s += image.size[0]*image.size[1]
    
        if i % 10000 == 0:
            print('iteration ', i)
    
    input("Press Enter to continue...")

    Input data: image dataset (10M small images) from Kaggle competition https://www.kaggle.com/c/avito-duplicate-ads-detection

    Result: It starts with 221MB for the file names.
    500 images - 374MB
    1M images - 510MB

    gc() inside the loop did not help.

  3. added this to the 3.4.0 milestone on Jul 11, 2016
  4. damaestro commented on Jul 14, 2016

    @damaestro

    Do we know what release this started in? I'd like to pin to a lower release to mitigate any production impact without having to set memory scaling alarm/actions.

  5. homm commented on Jul 14, 2016

    @homm
    Member

    @dmpetrov I can't reproduce this on Ubuntu 14.04 with Python 2.7 on Pillow master. Could you tell your Pillow, Python, OS, libjpeg versions?

    @damaestro This is not confirmed yet.

  6. homm commented on Jul 14, 2016

    @homm
    Member

    @dmpetrov I've tried Python 2.7 and 3.4 under OS X. I can confirm the leak in 3.4.

  7. changed the title [-]Memory is slowly leaking[/-] [+]Memory is slowly leaking in Python 3.x[/+] on Jul 14, 2016
  8. homm commented on Jul 14, 2016

    @homm
    Member

    So, this is a minimal test case, which I came to:

    https://gist.github.com/homm/c6a79ce7b445f47a74a4d296742a5af8

    As you can see, there is no Pillow at all! I'm pretty sure this is pure Python 3 memory leak.

    The script accepts path to the root folder with tons of files as argument and iterates all files in the folder twice: First time it opens the files and calls .close() method. At this stage, memory usage doesn't grow. Second time the script doesn't call .close() method and memory usage constanlty increasing. I've tested Python 3.4 and 3.5 on OS X and Python 3.4 on Ubuntu 14.04. It doesn't appear on Python 2.7.

    This is Python's memory leak because there are no pointers to the file objects from the application space. This is most likely only memory leak. At least lsof doesn't show any opened files after script's end. This leak appears only on different files. It doesn't work if we are reopening one file again and again. Average memory consumption is 370 bytes per file.

    As I understand we can't fix it on Pillow's level because we are assuming that file pointer may be shared with the other code. There are two possibilities: It can be fixed in Python itself. Or it can be fixed on the application level. Something like this:

    f = open(filename, 'rb')
    image = Image.open(f)
    image.load()
    f.close()

    instead of Image.open(filename)

  9. homm commented on Jul 14, 2016

    @homm
    Member

    I want to Invite @asvetlov to the thread. Maybe he could clarify something.

  10. hugovk commented on Jul 14, 2016

    @hugovk
    Member

    What happens when using with open(file.path, 'rb', 0) as f:?

  11. dmpetrov commented on Jul 14, 2016

    @dmpetrov
    Author

    @homm thank you for the investigation. It is becoming more and more interesting

    Yes, I use python 3.5. As far as I know, Python does not guarantee that file will be closed. So, it might be Python 3+ "feature", not a bug.

    It would be great to have opinions of Python experts.

  12. homm commented on Jul 14, 2016

    @homm
    Member

    Python does not guarantee that file will be closed.

    Of course it guarantees. Moreover, it guarantees that exactly .close() method will be called. It doesn't guarantee when this will be done, that is all.

    But as I said we are not speaking about the file descriptors leak. All files are closed. The problem is there is a memory leak.

  13. damaestro commented on Jul 14, 2016

    @damaestro

    I'm seeing the issue with Py 2.7 running thumbor, so I don't think this is isolated to Py 3.x.

    screenshot from 2016-07-14 15-22-59

  14. homm commented on Jul 14, 2016

    @homm
    Member

    @damaestro

    so I don't think this is isolated to Py 3.x.

    Any reasons why you are thinking this is the same leak? Memory leaks are not isolated to Python 3 or Python or any other platform or library. In this thread, we are discussing leak in Python 3 which affects Pillow users. You can report thumbor leaks in appropriate place.

  15. homm commented on Jul 14, 2016

    @homm
    Member

    @hugovk with file statement correctly frees all memory like .close() do.

  16. 30 remaining items

  17. radarhere commented on Nov 1, 2019

    @radarhere
    Member

    Support has now been removed for implicitly closing an image's file - #3577

  18. SaschaHeyer commented on Aug 21, 2020

    @SaschaHeyer

    It's hard to follow this issue can someone explain what is the suggested solution?

  19. radarhere commented on Aug 21, 2020

    @radarhere
    Member

    @SaschaHeyer I think the problem discussed here is likely resolved. If you have a situation, I'd recommend that you open a new issue with a self-contained example

  20. SaschaHeyer commented on Aug 21, 2020

    @SaschaHeyer

    @radarhere
    Thank you for your quick response.

    I wanted to know how the issue is resolved.
    What steps are required to solve?

  21. radarhere commented on Aug 21, 2020

    @radarhere
    Member

    If you are using the latest version of Pillow, then make sure that you close images properly. Either by explicitly calling close(),

    im = Image.new("RGB", (100, 100))
    im.close()

    or using a context manager

    with Image.open("hopper.jpg") as im:
        pass
  22. hugovk commented on Aug 30, 2020

    @hugovk
    Member

    Let's close this, and we can re-open if needed, or open a new issue.

  23. radarhere commented on Jan 12, 2021

    @radarhere
    Member

    @radarhere
    Thank you for your quick response.

    I wanted to know how the issue is resolved.
    What steps are required to solve?

    To clarify, I meant that I suspect it is resolved in the current version of Pillow, fixed by changes to our code, not to yours.

  24. Mvbbb commented on Apr 16, 2024

    @Mvbbb

    Is this problem got fixed in python2.7 pillow6.2.2

  25. radarhere commented on Apr 16, 2024

    @radarhere
    Member

    It's not completely clear in this issue what the problem was, or if/how it was fixed. Here are some notes though

  26. Mvbbb commented on Apr 16, 2024

    @Mvbbb

    It's not completely clear in this issue what the problem was, or if/how it was fixed. Here are some notes though在这个问题上,并不完全清楚问题是什么,或者是否/如何解决。不过,这里有一些注意事项

    Our service encountered some memory leaks and continuous memory growth when using Pillow to extract frames from webp animations. Can you identify any potential issues with the following code? Deeply grateful!

    • python: 2.7.9
    • pillow: 6.2.2
    def extract_gif_use_pillow(image):
        frames = []
        try:
            with Image.open(StringIO(image)) as im:
                with BytesIO() as frame:
                    while True:
                        try:
                            frame.seek(0)
                            im.save(frame, format='WEBP', quality=100)
                            frames.append(frame.getvalue())
                            # move to next frame
                            im.seek(im.tell() + 1)
                        except EOFError:
                            break
        except Exception as e:
            logger.error('extract_gif_use_pillow got exception: %s', e)
        gc.collect()
        logger.info('extract_gif_use_pillow got frames num: %s', len(frames))
        return frames
  27. radarhere commented on Apr 16, 2024

    @radarhere
    Member

    I see no obvious problems, but I do recommend that you use a version of Python that is still maintained, and a more recent version of Pillow. Pillow has also made significant improvements to how it reads GIF images.

    You may like to have a read of #7935 (comment). It is possible that is the explanation for your memory issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BugAny unexpected behavior, until confirmed feature.MemoryNumPyPlatformA catchall for platform-related

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions