【发布时间】:2018-10-16 15:30:15
【问题描述】:
我想要一种快速而肮脏的方法来获取一些文件名,而无需在我的 shell 中输入,所以我有以下这段代码:
from tkinter.filedialog import askopenfile
file = askopenfile()
现在一切正常,但它确实创建了一个多余的tkinter GUI,需要关闭。我知道我可以这样做来抑制它:
import tkinter as tk
tk.Tk().withdraw()
但这并不意味着它没有装在背面。这只是意味着现在有一个我无法关闭/销毁的 Tk() 对象。
所以这让我想到了我真正的问题。
似乎每次我创建一个Tk(),无论我是del 还是destroy(),内存都没有被释放。见下文:
import tkinter as tk
import os, psutil
process = psutil.Process(os.getpid())
def mem(): print(f'{process.memory_info().rss:,}')
# initial memory usage
mem()
# 21,475,328
for i in range(20):
root.append(tk.Tk())
root[-1].destroy()
mem()
# 24,952,832
# 26,251,264
# ...
# 47,591,424
# 48,865,280
# try deleting the root instead
del root
mem()
# 50,819,072
正如所见,即使在 Tk() 的每个实例被销毁并删除 roots 之后,python 也不会释放使用量。然而,其他对象并非如此:
class Foo():
def __init__(self):
# create a list that takes up approximately the same size as a Tk() on average
self.lst = list(range(11500))
for i in range(20):
root.append(Foo())
del root[-1]
mem()
# 52,162,560
# 52,162,560
# ...
# 52,162,560
所以我的问题是,为什么Tk() 和我的Foo() 不同,为什么销毁/删除创建的Tk() 不会释放占用的内存?
有什么明显的我错过了吗?我的测试不足以证实我的怀疑吗?我在这里和谷歌搜索过,但几乎没有找到答案。
编辑:以下是我尝试过(但失败了)的其他一些方法以及 cmets 中的建议:
# Force garbage collection
import gc
gc.collect()
# quit() method
root.quit()
# delete the entire tkinter reference
del tk
【问题讨论】:
-
我认为垃圾收集器按照自己的方式运行。您可以在销毁 tk 实例后尝试强制 GC。看看有没有帮助。
-
看看this post。 Alex Martelli 指出 python 使用了名为
free list的东西,这可能会导致您看到的内存问题。似乎有一种方法可以通过使用子流程来解决这个问题,所以如果这对你来说是个大问题,请调查一下。 -
调用
Tk()所做的远远超过,而不仅仅是创建一个您可能不想要的窗口;它正在加载和初始化一个完全独立的编程环境,即实际实现所有 GUI 功能的 Tcl 解释器。对这个解释器保留一个隐式引用,以便Tkinter模块中的函数可以真正完成它们的工作。 -
@Mike-SMT 这本身不是“问题”。诚然,Python 不会将内存返回给操作系统,但它可供将来使用,而不是向操作系统请求更多内存。
-
Tkinter 的“标准用法”是 GUI 在应用程序的整个生命周期中持续存在(事实上,是应用程序),所以没有意义能够更早地摧毁它。 Tcl 解释器完全有可能甚至没有实现在进程终止之前彻底关闭自己的方法,因为这在本机 Tcl 应用程序中完全没有意义。
标签: python memory tkinter psutil