Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

336
Views
Heroku web dyno running Django app not releasing memory

I'm struggling with a memory issue on Heroku when running a Django application (with gunicorn).

I have the following code that takes a user-uploaded image, removes all EXIF data, and returns the image ready for it to be uploaded to S3. This is used both as a form data cleaner and when reading base64 data into memory.

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

I basically take an InMemoryUploadedFile and return a new one with just the pixel data.

del and closes may be redundant, but they represent my attempt to fix the situation where Heroku memory usage keeps growing and is not released every time this function terminates, even remaining overnight: Heroku metrics

Running this on localhost with Guppy and following the tutorial, there are no remaining InMemoryUploadedFiles nor StringIOs nor PIL Image left in the heap, leaving me puzzled.

My suspicion is Python does not release the memory back to the OS, as I've read in multiple threads on SO. Has anyone played around with InMemoryUploadedFile and can give me an explanation as to why this memory is not being released?

When I do not perform this sanitisation, the issue does not occur.

Thanks a lot!

about 4 years ago · Santiago Trujillo
2 answers
Answer question

0

I think the issue is creating the temporary list object:

data = list(image.getdata())

Try:

image_without_exif.putdata(image.getdata())


This is why I think that is the issue:

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

Python memory usage increased ~4Mb.

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

No memory increase.

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

Python memory usage increased by ~85Mb.


You probably don't want to make getdata() into a list unless you need the numbers explicitly. From the Pillow docs:

Note that the sequence object returned by this method is an internal PIL data type, which only supports certain sequence operations. To convert it to an ordinary sequence (e.g. for printing), use list(im.getdata()).

about 4 years ago · Santiago Trujillo Report

0

I found my own answer eventually. Huge thanks to Ryan Tran for pointing me in the right direction. list() does indeed cause the leak.

Using the equivalent split() and merge() method (docs) this is the updated code:

 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
about 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!