【发布时间】:2013-06-27 08:13:17
【问题描述】:
我目前正在用 Python 编写一个简单的棋盘游戏,我刚刚意识到垃圾收集不会在重新加载图像时从内存中清除丢弃的位图数据。它仅在游戏启动或加载或分辨率发生变化时发生,但它会成倍消耗内存,所以我不能让这个问题得不到解决。
重新加载图像时,所有引用都会转移到新的图像数据,因为它与原始图像数据绑定到的变量相同。我尝试使用 collect() 强制垃圾回收,但没有帮助。
我写了一个小样本来演示我的问题。
from tkinter import Button, DISABLED, Frame, Label, NORMAL, Tk
from PIL.Image import open
from PIL.ImageTk import PhotoImage
class App(Tk):
def __init__(self):
Tk.__init__(self)
self.text = Label(self, text = "Please check the memory usage. Then push button #1.")
self.text.pack()
self.btn = Button(text = "#1", command = lambda : self.buttonPushed(1))
self.btn.pack()
def buttonPushed(self, n):
"Cycle to open the Tab module n times."
self.btn.configure(state = DISABLED) # disable to prevent paralell cycles
if n == 100:
self.text.configure(text = "Overwriting the bitmap with itself 100 times...\n\nCheck the memory usage!\n\nUI may seem to hang but it will finish soon.")
self.update_idletasks()
for i in range(n): # creates the Tab frame whith the img, destroys it, then recreates them to overwrite the previous Frame and prevous img
b = Tab(self)
b.destroy()
if n == 100:
print(i+1,"percent of processing finished.")
if n == 1:
self.text.configure(text = "Please check the memory usage now.\nMost of the difference is caused by the bitmap opened.\nNow push button #100.")
self.btn.configure(text = "#100", command = lambda : self.buttonPushed(100))
self.btn.configure(state = NORMAL) # starting cycles is enabled again
class Tab(Frame):
"""Creates a frame with a picture in it."""
def __init__(self, master):
Frame.__init__(self, master = master)
self.a = PhotoImage(open("map.png")) # img opened, change this to a valid one to test it
self.b = Label(self, image = self.a)
self.b.pack() # Label with img appears in Frame
self.pack() # Frame appears
if __name__ == '__main__':
a = App()
要运行上面的代码,您需要一个 PNG 图像文件。我的 map.png 的尺寸是 1062×1062。作为 PNG,它是 1.51 MB,作为位图数据,它大约是 3-3.5 MB。使用大图轻松查看内存泄漏。
运行我的代码时的预期结果:python 的进程一个周期地消耗内存。当它消耗大约 500 MB 时,它会崩溃,但会再次开始消耗内存。
请给我一些建议如何解决这个问题。我很感激每一个帮助。谢谢你。提前。
【问题讨论】:
-
首先,消耗500MB真的有问题吗?就此而言,500MB 只是虚拟内存还是物理/常驻内存? Python 通常不会将内存返回给操作系统;当您以后需要它时,它会保留它以供重复使用。这通常会使事情变得更快——一遍又一遍地分配、释放和重新分配数十 MB 需要大量时间。另外,你是在什么平台上的?例如,在 64 位 OS X 上,大多数进程以数百 MB 的 VM 结束,而在 32 位 linux 上,这种情况要少得多。
-
我不知道是物理内存还是 VRAM。我对编程很陌生,我不知道有什么工具可以检查。你能推荐一些吗?我使用命令行和任务管理器中的任务列表来跟踪内存消耗。我的操作系统是 Win7 x64。所以你说,只要偶尔崩溃就不是问题了?那将是一种解脱。
-
TaskManager 显示物理和虚拟内存的单独数字,但我不记得它们的确切名称。无论如何,如果您认为可能存在实际问题,则必须先了解 Windows 中的内存管理是如何工作的,然后才能进行调查。如果你没有任何实际问题,只是担心而不知道为什么,那就别担心了。
标签: python memory-leaks python-3.x tkinter pillow