enthought - 如何使用 View 避免内存泄漏

标签 enthought traitsui

使用以下程序:

from traits.api import HasTraits, Int, Instance
from traitsui.api import View

class NewView(View):
    def __del__(self):
        print('deleting NewView')

class A(HasTraits):
    new_view = Instance(NewView)
    def __del__(self):
        print('deleting {}'.format(self))

    a = Int

    def default_traits_view(self):
        new_view = NewView('a')
        return new_view

运行
a = A()
del(a)

返回
 deleting <__main__.A object at 0x12a016a70>

正如它应该。

如果我做
a = A()
a.configure_traits()

关闭对话框后:
del(a)

我有同样的消息:
deleting <__main__.A object at 0x12a016650>

没有提到要删除的 NewView。

总的来说,使用 Traits 和 TraitsUI 避免内存泄漏的好做法是什么?

最佳答案

这里发生的事情是NewView object 参与一个引用循环,并且该循环中的对象不会作为 CPython 的主要基于引用计数的对象释放机制的一部分自动收集。然而,它们最终应该作为 CPython 循环垃圾收集器的一部分被收集,或者您可以通过执行 gc.collect() 来强制收集。 ,所以这里应该没有实际的长期内存泄漏。

具有讽刺意味的是,试图通过添加 __del__ 来检测最终的集合。方法 NewView阻碍进程,因为它呈现 NewView对象不可收集:至少在 Python 2 中,Python 不会尝试收集包含具有 __del__ 的对象的循环。方法。见 gc docs详情。 (由于 PEP 442 中概述的更改,Python 3 在这里更聪明一些。)所以对于 __del__方法,使用Python 2,确实会随着时间的推移出现缓慢的内存泄漏。解决方法是删除__del__方法。

这是一个显示引用循环的图(实际上,这显示了包含 NewView 对象的对象图的整个强连接组件):节点是所涉及的对象,箭头从引用者指向被引用者。在图表的右下部分,您会看到 NewView对象具有对其顶级 Group 的引用(通过 content 属性),以及 Group对象具有对原始 View 的引用(container 属性)。在 View 的其他地方也有类似的循环。

NewView reference cycle

可能值得在 Traits UI 跟踪器上打开功能请求:理论上,当不再需要 View 时,应该可以手动中断引用循环,尽管在实践中可能需要对 Traits UI 源进行大量修改。

下面的代码演示了(删除了 __del__ 方法)对 gc.collect 的调用确实收集了 NewView对象:它存储对 A 上的 View 的弱引用例如,带有一个回调,该回调报告该 View 何时被垃圾收集。

from traits.api import HasTraits, Int, Instance
from traitsui.api import View

import gc
import weakref

class NewView(View):
    pass


def report_collection(ref):
    print("NewView object has been collected")


class A(HasTraits):
    a = Int

    def default_traits_view(self):
        new_view = NewView('a')
        self.view_ref = weakref.ref(new_view, report_collection)
        return new_view


def open_view():
    a = A()
    a.configure_traits()
    print("Collecting cyclic garbage")
    gc.collect()
    print("Cyclic garbage collection complete")

在我的机器上,这是我在 open_view 时看到的叫做:
>>> open_view()
Collecting cyclic garbage
NewView object has been collected
Cyclic garbage collection complete

关于enthought - 如何使用 View 避免内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40840033/

相关文章:

python - 为什么用户输入的 Traitsui 中的属性不更新

enthought - TabularAdapters 的列宽?

python - Traitsui 阻止 txt-box 实时更新

ipython - Canopy 中的 QT Shell

python - 使用不带 GUI 的 Canopy

wxpython - Mayavi:帮助重置鼠标选择器并将 wx 事件连接到@on_trait_change

python - 更改 Array 元素时在 Python traits 包中触发事件

python - 动态添加图例到 matplotlib 图

python - numpy.random 没有属性 'choice'

python - 无法在 windows、python 上安装 traits