我遇到了内存泄漏的问题,因此我做了以下测试代码。
#!/usr/bin/cjs
"use strict";
import Gtk from 'gi://Gtk?version=3.0';
Gtk.init (null);
const window= new Gtk.Window ();
let buttons=[], button, box=new Gtk.Box ({ orientation:1}), remove=true;
function construct_buttons ()
{
for (let i=0;i<500;i++)
{
button=new Gtk.Button ({label:"hello"});
box.add (button);
buttons.push (button);
}
}
setInterval (function ()
{
if (remove)
{
for (let i=0;i<500;i++) buttons[i].destroy ();
buttons=[], button=null;
}
else
{
construct_buttons ();
window.show_all ();
}
remove=!remove;
}, 2000);
construct_buttons ();
window.add(box);
window.set_title ("Test");
window.connect ('destroy', () => { Gtk.main_quit (); });
window.set_size_request (740, 600);
window.show_all ();
Gtk.main ();
我构造了 500 个按钮并将其添加到一个框中。2 秒后,我使用 Gtk.widget.destroy() 销毁它们并释放引用数组 buttons=[], button=null,2 秒后我再次构造它们,依此类推。
这样做了一段时间后,我的内存从 17MiB 增长到了 50MiB。然后我停止了。我不知道我做错了什么。我在更大的应用程序中也遇到了同样的问题。这只是一个用于测试的小例子。
我也尝试过用 Gtk.widget.remove(widget) 来代替 destroy,然后让 cjs 销毁它,但这里内存也会增大。
我有 cjs 版本 6.0.0
我使用heapgraph工具分析了原始程序。删除 500 个按钮后,它们在堆转储中可见,但标记为“无法访问”。因此它们没有泄漏,它们只会在下一次垃圾收集中被删除。
问题似乎是垃圾收集器未被触发。这可能是已知问题https://gitlab.gnome.org/GNOME/gjs/-/issues/52。目前,您可以通过
imports.system.gc()
在销毁按钮后添加并清空引用来手动触发垃圾收集。如果我将该行添加到原始脚本中,即使以两倍的速度创建按钮并运行 10 分钟,内存使用量也不会超过 19 MB。好的,经过几个小时的测试,我明白了。
我必须在 destroy 信号处理程序中释放 GJS 持有的引用,所有其他尝试都会导致内存泄漏。
我不知道为什么,但在使用 Gtk.widget.destroy() 后简单地将 GJS 中的引用设置为 null 不起作用,如果您尝试先从父级中删除元素,然后将其引用设置为 null 并让 GJS 销毁它,也会导致内存泄漏。
这有效:
因此,如果我构建自己的小部件(由许多 Gtk 小部件组成),我会将所有小部件收集到“小部件”命名空间容器对象中。
构建后,我将每个小部件的处理程序连接到销毁信号,并在销毁处理程序中释放引用。
我不知道这是否是正确的方法,但它有效。
一个更复杂的例子: