我的程序使用了大量 ram 内存,我该怎么办?

My program uses a lot of ram memory, what can I do?

我的程序使用了很多 ram 内存,它的作用是这样的:

  1. 从在线相机下载图像。
  2. 在 window (Tkinter window)
  3. 中显示图像
  4. 删除图像。
  5. 再次相同的过程(无限循环直到 window 关闭)

这是代码:

def show_image(self, cam):
    while True:
        start_time = time.time()
        self.update_image(cam)

        load = Image.open(cam.getPlace() + "/image.jpeg")
        load.thumbnail((400, 300))
        render = ImageTk.PhotoImage(load)

        if cam.getPlace() == "FrontYard1":
            try:
                img1 = Label(image=render)
                img1.image = render
                img1.grid(row=0, column=0)
            except:
                print("Error")
        elif cam.getPlace() == "FrontYard2":
            try:
                img2 = Label(image=render)
                img2.image = render
                img2.grid(row=0, column=1)
            except:
                print("Error")
        elif cam.getPlace() == "Garden1":
            try:
                img3 = Label(image=render)
                img3.image = render
                img3.grid(row=1, column=0)
            except:
                print("Error")
        else:
            try:
                img4 = Label(image=render)
                img4.image = render
                img4.grid(row=1, column=1)
            except:
                print("Error")

        print("And " + str(time.time() - start_time) + " to run show image")

def update_image(self, cam):
    os.remove(cam.getPlace()+"/image.jpeg")
    if cam.getModel() == "Model":
        urllib.request.urlretrieve(#link and folder to download the image)
    else:
        urllib.request.urlretrieve(#link and folder to download the image)

我尝试使用 gc.collect() 但它似乎不起作用。

快速查看后,如果您删除第二步而不实际显示它并直接将其保存到计算机,这将有助于减少 ram。您是否有任何关于您有多少 RAM 以及计算机在此过程中使用了多少的详细信息?另一个可能是添加一个步骤来将图像格式化为特定大小,因为它可能太大而无法处理大量数据。

您的代码试图创建无限数量的 Label 对象,将每个对象隐藏在无限堆其他标签后面。这显然会导致问题,除非你有无限的内存。

想要的在任何给定时间都只是四个标签。您需要跟踪您创建的标签,理想情况下只是重复使用它们,或者,如果没有,销毁它们而不是仅仅将它们隐藏在新标签后面。


目前,您的代码正在跟踪 img1img4 变量中的标签,只是它们在第一次显示图像时才会创建,因此您可以'检查它们。因此,在循环之前,您要么创建四个空标签,要么只将变量设置为 None。然后,在循环内,您可以这样做(假设您使用 None 而不是空标签):

    if cam.getPlace() == "FrontYard1":
        try:
            if img1 is None:
                img1 = Label(image=render)
                img1.grid(row=0, column=0)
            else:
                img1.image = render
        # etc.

您可以通过将标签存储在字典中而不是四个单独的变量中来简化此操作:

imgs = {“Front Yard 1": img1, …}

… 然后用字典查找替换 if/elif 链:

img = imgs.get(cam.getPlace())

尽管在这里,您几乎肯定会想要使用空标签而不是 None 作为初始值。


附带说明一下,您的程序还有另一个非常严重的问题。 while True: 循环显然永远不会 returns 到 tkinter 事件循环。这意味着您不给 tkinter 机会更新显示,或响应鼠标事件或退出或来自 window 系统的其他事件,因此您的应用程序将无响应并弹出沙滩球或沙漏或其他。

要解决此问题,您需要删除循环,而让您的方法仅检索和处理一张图像,然后调用 after 以要求 tkinter 下次通过事件循环再次调用它。

这还需要将那些局部 img1 变量(或上面建议的字典 imgs)更改为实例变量。

如果您尝试为此任务使用后台线程,则从除主线程之外的任何线程对 tkinter 小部件执行任何操作都是非法的。在某些平台上,这只会立即给您一个错误,或者挂起 GUI。在其他平台上,它似乎可以工作,但偶尔会做一些奇怪的事情——这更糟糕。

如果你正在解决这个问题,例如,mtTkinter 上的 Python 3 分支(它包装了 the Effbot Book 中显示的 Queue 逻辑,所以一切你在一个小部件上所做的实际上是发布一条消息供主线程处理),那么你可以忽略这一部分。但如果没有,你要么想这样做,要么手动做同样的事情(如链接页面所示),或者只是摆脱后台线程并使用 after.


当我们这样做的时候,一个只打印 Error 的裸 except: 隐藏了任何可能出现的问题并使调试变得更加困难,所以你几乎不应该这样做.

另外,Label.image assignment 真的只有这里可以加注吗?


综合起来:

def __init__(self):
    self.imgs = {}
    img1 = Label()
    img1.grid(row=0, column=0)
    self.imgs["Frontyard1"] = img1
    # ... likewise for 2-4

def show_image(self, cam):
    start_time = time.time()

    try:
        self.update_image(cam)

        load = Image.open(cam.getPlace() + "/image.jpeg")
        load.thumbnail((400, 300))
        render = ImageTk.PhotoImage(load)

        img = self.imgs.get(cam.getPlace())
        if img:
            img.image = render
        # What do you want to do if it's not one of the four expected?
        # Your existing code just ignores the image but still counts time,
        # so that's what I did here.

    except Exception as e:
        print(f"Error updating cam '{cam.getPlace()}: {e!r}")
    else:
        print("And " + str(time.time() - start_time) + " to run show image")

    self.root.after(0, self.show_image, cam)