Python Forum
Images are storing in RAM and don't get garbage collected - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Images are storing in RAM and don't get garbage collected (/thread-31848.html)



Images are storing in RAM and don't get garbage collected - MaxRicik - Jan-06-2021

I have a random image picker, and images created by PIL are not garbage collected, even if I delete self.rand_img.image. I can collect it by destroying self.rand_img, but it's very inconvenient. It's there any other way, how to solve this?

Here's my code.
import os
import random
from PIL import Image, ImageTk
import tkinter as tk

class App:

def __init__(self):

    def load_imgs(path=str()):

        for i in os.listdir(self.path + path):

            if i.endswith((".jpg", ".jpeg", ".png")): self.pics.append(path + i)
            elif os.path.isdir(self.path + path + i): load_imgs(path + i + "\\")

    self.w = None
    self.h = None
    self.pics = []
    self.path = input() + "\\"
    self.root = tk.Tk()
    self.root.columnconfigure(0, weight=1)
    self.root.rowconfigure(0, weight=1)

    load_imgs()

    print(len(self.pics))

    self.rand_img = tk.Label(self.root, bg="red")
    self.rand_img.grid(row=0, column=0, sticky="nwse")
    self.rand_img.update()
    self.new_img()

    self.btn = tk.Button(self.root, text="Next", command=self.new_img)
    self.root.bind("<Return>", self.new_img)
    self.btn.grid(row=1, column=0)

    self.root.mainloop()

def new_img(self, event=None):

    def resize(event=None, img=None):

        w = img.size[0]
        h = img.size[1]

        self.rand_img.update()

        if w > h:
            
            master = w / self.rand_img.winfo_width()
            w = self.rand_img.winfo_width()
            h /= master

        elif h > w:
            
            master = h / self.rand_img.winfo_height()
            h = self.rand_img.winfo_height()
            w /= master

        if w > self.rand_img.winfo_width():

            master = w / self.rand_img.winfo_width()
            w = self.rand_img.winfo_width()
            h /= master

        elif h > self.rand_img.winfo_height():

            master = h / self.rand_img.winfo_height()
            h = self.rand_img.winfo_height()
            w /= master

        if self.w != w or self.h != h:
        
            img = img.resize((int(w), int(h)))
            img_tk = ImageTk.PhotoImage(image=img)
            self.rand_img.configure(image=img_tk)
            self.rand_img.image = img_tk
            print(self.rand_img.image)

        self.w = w
        self.h = h

    img = Image.open(self.path + self.pics[random.randint(0, len(self.pics))])
    resize(img=img)

    self.rand_img.bind("<Configure>", lambda event: resize(event, img=img))


app = App()



RE: Images are storing in RAM and don't get garbage collected - Larz60+ - Jan-06-2021

Garbage is not necessarily collected immediately, rather as the space is needed, some objects can not be collected if they can't be reached.
There is, however, an alternative garbage collector (gc). See: https://docs.python.org/3/library/gc.html


RE: Images are storing in RAM and don't get garbage collected - MaxRicik - Jan-06-2021

(Jan-06-2021, 06:05 PM)Larz60+ Wrote: Garbage is not necessarily collected immediately, rather as space is needed, some objects can not be collected if they can't be reached.
There is, however, an alternative garbage collector (gc). See: https://docs.python.org/3/library/gc.html

I don't know exactly if it has something common with garbage collecting, but Tkinter doesn't clean up images, that aren't used, and it's using all my RAM after some time. I really don't know, how to solve it without destroying Label, because in that way it's super glitchy.


RE: Images are storing in RAM and don't get garbage collected - Larz60+ - Jan-06-2021

Tkinter author was Fredrik Lundh.
Not sure of his email, but perhaps contact him.


RE: Images are storing in RAM and don't get garbage collected - deanhystad - Jan-07-2021

It would be nice if you provided an example that works.

Usually the complaint is that Tkinter is too eager to throw away images. A lot of folks will create an image in a function, assign the image to a button or label, and expect the image to appear. Unless you keep some handle to the image it is marked for garbage collection as soon as the function exits.

My guess is you are making something that references the object, something that hangs around when you load a new image. del will not fix this problem. In the example below I create a string, reference the string by two variables, and attempt to delete the string. The delete does not work because there are still references tot he string.
import sys

x = 'this is a string'
y = x
print(id(x), sys.getrefcount(x))

del x
print(y)
print(id(y), sys.getrefcount(y))

print(x)
Output:
1803236043312 4 this is a string 1803236043312 3 Traceback (most recent call last): File "C:\Users\hystadd\Documents\python\sandbox\junk.py", line 11, in <module> print(x) NameError: name 'x' is not defined
"del x" just deletes the variable x and decrements the reference count for the string. y still references the string and the string still exists because y references the string.

Try to make a simpler example that other people can run. Doing so you will likely find the error in you code that prevents freeing unused images. That deleting self.rand_img fixes the problem is a strong indication that self.rand_image is where the problem lies. There is something there that is hanging on to the old image, or you are keeping multiple rand_img things around.