C 对话框创建和销毁循环增加内存使用

C 对话框创建和销毁循环增加内存使用,c,windows,memory-leaks,gtk,gtk3,C,Windows,Memory Leaks,Gtk,Gtk3,我编写了一个简单的程序模块,要求用户输入配置文件名。为此,我创建了带有entry小部件的windoww,并在网格中组织了两个按钮Ok和Cancel。当用户输入一个已经存在的配置文件名时,它会通过使用“确定”按钮创建对话框来通知用户这一事实,当用户按下该按钮后,它会返回到选择一个配置文件名,同时窗口不会被隐藏或破坏。问题是,当我创建一个配置文件,然后通过在enter键上放置一些重的东西来垃圾邮件发送ok按钮,然后在配置文件名称选择器和对话框上泡茶,通过创建和销毁对话框进行简单循环,程序的内存使用量

我编写了一个简单的程序模块,要求用户输入配置文件名。为此,我创建了带有entry小部件的windoww,并在网格中组织了两个按钮Ok和Cancel。当用户输入一个已经存在的配置文件名时,它会通过使用“确定”按钮创建对话框来通知用户这一事实,当用户按下该按钮后,它会返回到选择一个配置文件名,同时窗口不会被隐藏或破坏。问题是,当我创建一个配置文件,然后通过在enter键上放置一些重的东西来垃圾邮件发送ok按钮,然后在配置文件名称选择器和对话框上泡茶,通过创建和销毁对话框进行简单循环,程序的内存使用量会增加

TL;博士 简单地创建和销毁gtk窗口和对话框似乎会导致内存泄漏。将应用程序置于循环中使其内存使用量从10mb增加到200mb,增加了约1900%

不,我没有使用专门为它设计的应用程序测试内存泄漏。 是的,我已经设置了G_SLICE=always malloc。 是的,有另一个线程在程序的后台运行,但我确信它不会导致任何泄漏 如果您想了解内存中发生的更多信息,我可以从Windows Performance Monitor发布屏幕

问题是-这是我造成的内存泄漏,还是GTK的错我听说它有一个懒惰的内存管理策略,但一段时间后内存使用率从200mb下降到140mb并保持不变

代码如下:

// This callback racts to the ok and cancel buttons. If input was correcs
// or the user pressed cancel it destroys the window. Else it show error
// prompt. The showing error prompt seems to be the problem here.
void pickNameButtCB(GtkWidget *button, gpointer *call)
{
    GtkWidget *window = gtk_widget_get_toplevel(button);
    if( *((char*)call) == 'k')
    {
        GList *entryBase = gtk_container_get_children(GTK_CONTAINER(gtk_bin_get_child(GTK_BIN(window)))), *entry = entryBase;
        for(size_t i=g_list_length(entry); i>0 && !GTK_IS_ENTRY(entry->data); --i)
            entry = g_list_next(entry);
        if(gtk_entry_get_text_length(GTK_ENTRY(entry->data)) > 0)
        {
            const char *temp = gtk_entry_get_text(GTK_ENTRY(entry->data));
            char path[266];
            strcpy(path, settingsDir);
            strcat(path, temp);
            strcat(path, ".prof");

            if(settProfExists(path))
            {
                g_list_free(entryBase);
                showError(GTK_WINDOW(window), GTK_MESSAGE_ERROR, "Profile with that name already exists!");
                return;
            }
            // nothing here executes as well
        }
        else
        {
            /** doesn't execute when the memory leak happens */
        }
        g_list_free(entryBase);
    }
    gtk_widget_destroy(window);
    gtk_main_quit();
}

void showError(GtkWindow *parent, GtkMessageType type, const char *str)
{
    GtkWidget *window = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_OK, str);
    g_signal_connect_swapped(window, "response", G_CALLBACK(gtk_widget_destroy), window);
    gtk_dialog_run(GTK_DIALOG(window));
}

bool settProfExists(const char *path)
{
    if(fileExists(path))
        return true;
    return false;
}

bool fileExists(const char *path)
{
    struct stat info;
    errno = 0;
    if((stat(path, &info)) != 0)
        if(errno == ENOENT)
            return false;
    return true;
}

您的代码太不完整,无法找到根本原因

尤其是这句话毫无意义。我们缺少entry的定义,您正试图在同一行*entry=entryBase;上执行多个操作

但是,泄漏可能发生在您调用的函数中,您没有提供代码settProfExists,newProfile。因此,请提供一个最小的可编译示例来重现这个问题

至于样式,有几个错误:您正在以处理数组的方式遍历列表。看看GList是如何实现的,你会发现这是一个愚蠢的提示:看看entry->prev和entry->next:

然后是一个架构问题:不要试图检查UI来猜测小部件的位置,只需创建一个结构,并在一个带有回调的gpointer data参数的结构中传递调用参数和您感兴趣的GtkEntry

您也不应该手工创建路径:您使用的是长度可能不够长的固定长度数组。使用GLib提供的可移植的g_build_路径,并在使用后使用g_free释放路径,这将为您处理Windows/Linux目录分隔符

对于您的对话框,当您运行gtk_run_dialog时,您可以直接调用gtk_destroy_widget,而不是连接信号:

GtkWidget *window = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_OK, str);
gtk_dialog_run(GTK_DIALOG(window));
gtk_widget_destroy(window);

为我调试代码不是一个好问题。从消除代码中多余的细节开始,直到达到一个目标。您自己可能会发现这个问题。我看到了您创建窗口的功能,但我没有看到任何窗口的实际创建或破坏。@JohnBollinger chooseProfName在这里只是为了完整性。它确实创建了在循环中不断打开的窗口。循环实际上在pickNameButtCB中,运行的是淋浴ROR,对话框在这里创建并销毁。我将响应信号连接到小部件销毁功能。如果您在消除混乱的同时无法找到问题,请发布一个问题。但我怀疑你迄今为止的失败很大程度上受到了大量细节的影响。这不是你上一次编辑中的一个问题。认真地说,花点时间减少它,即删除代码而不是不耐烦。。。我不喜欢在NULL出现之前进行搜索,但我认为这会释放一个寄存器,所以gcc会有更好的优化可能性。还有,为什么要检查GUI,以找出我知道的不好的地方?我的意思是,创建一个结构仅仅是为了显示一个在程序中不再使用的条目对话框,这有什么意义吗?至于g_构建路径。。。现在有点太晚了。我最近意识到了油嘴滑舌的力量,一旦我开始重构项目,我就会记住它。是的,我可以破坏小部件而不是连接信号,谢谢。这不仅仅是一个优化问题。您的代码不应该依赖于UI的组织方式,因此您可以在不破坏代码的情况下移动小部件并改善用户体验。至于优化,您的示例不会扩展:在一个包含100个元素的UI上,遍历它仍然有意义吗?不,你不应该尝试内省UI,除非你真的有很好的理由这样做,比如写一个像gtk inspector这样的内省工具。过早的优化也是万恶之源。只需调用g_list_length遍历整个列表一次,然后在循环中第二次遍历它。除非你确定你的算法是最优的,否则不要在寄存器级别思考。
   for(size_t i=g_list_length(entry); i>0 && !GTK_IS_ENTRY(entry->data); --i)
        entry = g_list_next(entry);
GtkWidget *window = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_OK, str);
gtk_dialog_run(GTK_DIALOG(window));
gtk_widget_destroy(window);