Python Forum
Images are storing in RAM and don't get garbage collected
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Images are storing in RAM and don't get garbage collected
#1
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()
Reply
#2
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
Reply
#3
(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.
Reply
#4
Tkinter author was Fredrik Lundh.
Not sure of his email, but perhaps contact him.
Reply
#5
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.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  threadlocals are garbage collected before thread ends akv1597 0 1,807 Mar-09-2021, 12:13 PM
Last Post: akv1597
  Why doesn't gc delete an object without forcing a garbage collection call? AlekseyPython 5 3,765 Mar-19-2019, 02:10 AM
Last Post: micseydel
  Taking user input and storing that to a variable then storing that variable to a list jowalk 12 37,380 Mar-27-2017, 11:45 PM
Last Post: wavic

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020