AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 79556357
Accepted
Schmaehgrunza
Schmaehgrunza
Asked: 2025-04-05 07:12:21 +0800 CST2025-04-05 07:12:21 +0800 CST 2025-04-05 07:12:21 +0800 CST

CJS 中的记忆增长

  • 772

我遇到了内存泄漏的问题,因此我做了以下测试代码。

#!/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

在此处输入图片描述

在此处输入图片描述

gjs
  • 2 2 个回答
  • 35 Views

2 个回答

  • Voted
  1. Best Answer
    ptomato
    2025-04-06T06:46:28+08:002025-04-06T06:46:28+08:00

    我使用heapgraph工具分析了原始程序。删除 500 个按钮后,它们在堆转储中可见,但标记为“无法访问”。因此它们没有泄漏,它们只会在下一次垃圾收集中被删除。

    问题似乎是垃圾收集器未被触发。这可能是已知问题https://gitlab.gnome.org/GNOME/gjs/-/issues/52。目前,您可以通过imports.system.gc()在销毁按钮后添加并清空引用来手动触发垃圾收集。如果我将该行添加到原始脚本中,即使以两倍的速度创建按钮并运行 10 分钟,内存使用量也不会超过 19 MB。

    • 1
  2. Schmaehgrunza
    2025-04-05T22:54:49+08:002025-04-05T22:54:49+08:00

    好的,经过几个小时的测试,我明白了。
    我必须在 destroy 信号处理程序中释放 GJS 持有的引用,所有其他尝试都会导致内存泄漏。
    我不知道为什么,但在使用 Gtk.widget.destroy() 后简单地将 GJS 中的引用设置为 null 不起作用,如果您尝试先从父级中删除元素,然后将其引用设置为 null 并让 GJS 销毁它,也会导致内存泄漏。

    这有效:

    #!/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 finalize_destroy (button)
        {
        buttons[button.index]=null;
        }
    
    function construct_buttons ()
       {
       for (let i=0;i<500;i++)
          {
          button=new Gtk.Button ({label:"hello"});
          box.add (button);
          button.index=i;
          button.connect ("destroy", finalize_destroy)
          buttons.push (button);
          }
       }
       
    setInterval (function ()   
       {
       if (remove)
          {
          for (let i=0;i<500;i++) buttons[i].destroy ();
          buttons=[];
          }
       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 ();
    

    因此,如果我构建自己的小部件(由许多 Gtk 小部件组成),我会将所有小部件收集到“小部件”命名空间容器对象中。
    构建后,我将每个小部件的处理程序连接到销毁信号,并在销毁处理程序中释放引用。
    我不知道这是否是正确的方法,但它有效。
    一个更复杂的例子:

    #!/usr/bin/cjs
    "use strict";
    import Gtk from 'gi://Gtk?version=3.0';
     
    Gtk.init (null);
    
    const window=  new Gtk.Window ();
    let top_box=new Gtk.Box ({ orientation:1}), own_widgets= [], remove=true;
    
    //-- constructor own widgets: which is a collection of structured gtk widgets
    function OwnWidget ()
       {
       var widgets=new Object ();
       
       //-- collecting all widgets i construct in container namespace widgets
       widgets.eventbox_for_hbox= new Gtk.EventBox ();                                                           
        widgets.eventbox_for_vbox= new Gtk.EventBox ({halign:1,valign:1});                                          
        widgets.hbox=              new Gtk.Box ({ spacing: 2});
        widgets.vbox=              new Gtk.Box ({ orientation:1, spacing: 2});
        widgets.button_A=          new Gtk.Button ({ label:"Button_A", halign:3, valign:3 }),         
        widgets.button_B=          new Gtk.Button ({ label:"Button_B", halign:3, valign:3 }), 
        widgets.button_C=          new Gtk.Button ({ label:"Button_C", halign:3, valign:3 }), 
       
       //-- some structure: eventbox_for_vbox (top container) -> vbox -> eventbox_for_hbox -> hbox -> buttons                                       
            widgets.hbox.add(widgets.button_A);
            widgets.hbox.add(widgets.button_B);
            widgets.hbox.add(widgets.button_C);
            widgets.eventbox_for_hbox.add (widgets.hbox);
        widgets.vbox.add (widgets.eventbox_for_hbox);
        widgets.eventbox_for_vbox.add (widgets.vbox);
        
        /* connecting destroy handler for every widget -> OwnWidget.prototype.destroy.finalize_destroy is the template function
           and adding a property to every widget called OWNWIDGET_propertyID, which is the widgets property name in the "widgets" object.
           This is needed in finalie_destroy see below. */
        for (let propertyID in widgets)
           {
           widgets[propertyID].connect ("destroy", OwnWidget.prototype.destroy.finalize_destroy.bind (this));
           widgets[propertyID].OWNWIDGET_propertyID=propertyID;
           }
           
        this.widgets=widgets;
        this.top_container=widgets.eventbox_for_vbox;
       }
    
    //-- destroys your own widget
    OwnWidget.prototype.destroy=function ()
       {
       this.top_container.destroy ();
       this.top_container=null;
       }
    
    //-- called on signal destroy, releaving the references of GJS in instance.widgets object
    OwnWidget.prototype.destroy.finalize_destroy=function destroy (widget)
       {
       this.widgets[widget.OWNWIDGET_propertyID]=null;
       }
       
    //-- construct 100 own widgets with the above structure
    function construct ()
       {
       let own_widget;
     
       for (let i=0;i<250;i++)
          {
          own_widget=new OwnWidget ();
          top_box.add (own_widget.widgets.eventbox_for_vbox);
          own_widgets[i]=own_widget;
          }
       window.show_all();
       }
    
    //-- removes the 100 own widgets from the top-box and destroys it
    function remove_and_destroy ()
       {
       for (let i=0;i<250;i++) 
          {
          own_widgets[i].destroy ();
          /* to be sure, that all is freed, i also set the array reference on the own widget instance null,
             but this will be overwritten by the next construct () call, so i think its not necessary. */
          own_widgets[i]=null;
          }
       }
     
    setInterval (function ()   
       {
       if (remove) remove_and_destroy ();
       else construct ();
    
       remove=!remove;
       }, 3000);
    
    construct ();
    window.add(top_box);
    window.set_title ("Test");
    window.connect ('destroy', () => { Gtk.main_quit (); });
    window.set_size_request (740, 600);
    window.show_all ();
    
    Gtk.main ();
    
    • 0

相关问题

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve