1

I am working on a python program, that fetches images via requests, displays them five at the time, updating one at a time with a given interval. A separate thread is responsible for updating the queue with fresh images once in a while. It is supposed to run on a raspberry pi (original model b with .5gb ram) running raspbian (debian wheezy).

I am writing this in python3 and using TKinter, Pillow and requests.

It works quite well - on my windows development machine. It runs fine on the pi as well, but after a while it gets slower at updating the images on the screen, and eventually it gets killed by linux. Checking /var/log/syslog reveals, that it is killed because the system runs out of memory.

Running it on windows, i noticed in the taskmanager, that the memory usage increases ~1mb every time an onscreen image is updated. Stepping through the code in debugging mode, i can see that the increase in memory usage happens in the following function:

def resize(self, img, width, height):
    img = img.resize((width, height), Image.ANTIALIAS)
    tkimg = ImageTk.PhotoImage(img)
    return tkimg

It seems like the img object hangs around. So my question is: Why does it hang around, when i assign the resized image to the same variable? Shouldn't the garbage collector handle this automatically?

Any help appreciated!

EDIT:
Wrapping the culprit lines in while True, memory usage does not increase with each pass. It seems to only increase when the function is called with a new img as argument.

    while True:
        img = img.resize((width, height), Image.ANTIALIAS)
        tkimg = ImageTk.PhotoImage(img)

Deleting the returned img object after having assigned it to the TKinter label also does not seem to have any effect.

esben
  • 11
  • 4
  • 1
    Note that reassigning `img` inside a function won't change the value of the object that you passed into the function in the first place. The old `img` will continue to exist as long as any living variable retains a reference to it. Whether this happens or not in your code is hard to say without seeing the entire program. – Kevin Sep 03 '15 at 13:38
  • I think the `resize()` method returns `None` anyway, so it's rather immaterial what you do with that return value. Images are backed by buffers that may be shared among multiple Python objects. It's a bit tricky to track down whether they get freed. You might want to take a look at http://stackoverflow.com/questions/1435415/python-memory-leaks – Sven Marnach Sep 03 '15 at 13:49
  • @SvenMarnach: If `Image.resize)()` returned `None` then the subsequent `tkimg = ImageTk.PhotoImage(img)` wouldn't create the PhotoImage object that Tkinter displays. According to [the Pillow docs](http://pillow.readthedocs.org/en/latest/reference/Image.html#PIL.Image.Image.resize) it returns a resized copy of the image. – PM 2Ring Sep 03 '15 at 14:14
  • Does adding a `gc.collect()` call help? When you execute only this line in a `while True:` loop, does it still run out of memory? – maxy Sep 03 '15 at 14:17
  • 1
    What Kevin said. The `img` on the LHS of `img = img.resize(...)` is a local reference to a new object, it doesn't replace the `img` passed into the function. My _guess_ is that something fishy is happening with the PhotoImage. You're supposed to keep a reference to the source PIL Image around when you make a PhotoImage, otherwise it gets blanked, as mentioned on the Tkinter unpythonic wiki [Disappearing PhotoImage](http://tkinter.unpythonic.net/wiki/PhotoImage). But you seem to be having the opposite problem, since you're _not_ keeping a reference but your images display ok. And that's weird! – PM 2Ring Sep 03 '15 at 14:30
  • But note in that doc: "Tk images are not first-class objects: when you apply one to a widget, you do not in any sense "pass a reference to" the image. Instead, you just pass a name, which Tk looks up in an internal table of defined images **(from which images can only be deleted manually)**." But then it goes onto say " If Tkinter didn't delete images when no Python reference to them existed, they would be unavoidable memory leaks. " TL;DR Tkinter is weird, especially ImageTk.PhotoImage. :) – PM 2Ring Sep 03 '15 at 14:33
  • @Kevin I'm quite sure I delete the reference when I'm done with it, but I am not on my own pc right now. I can post some more code when I get home. – esben Sep 03 '15 at 15:59
  • @maxy I actually do a gc.get_count followed by gc.collect followed by another get_count. The count varies a bit, bit it isn't drastic. It seems to stay below 100. Will try running it while True when I get home. (sorry about formatting - I'm on a touch keyboard...) – esben Sep 03 '15 at 16:04
  • @PM2Ring I do in fact keep a reference to the image returned by the function, which is what I insert on the Label with config(...). but its still weird! :) – esben Sep 03 '15 at 16:06

0 Answers0