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

时间:2017-02-08 16:22:25

标签: c windows memory-leaks gtk gtk3

我写了一个简单的程序模块来询问用户的个人资料名称。为此,我创建了带有条目小部件的windoww,以及在网格中组织的两个按钮(Ok和Cancel)。当用户输入已经存在的配置文件名称时,它通过创建" ok"的对话框来通知他该事实。按钮,然后按下它后,它返回选择一个配置文件名称(同时窗口没有被隐藏或销毁)。问题是,当我创建一个配置文件,然后在配置文件名称选择器和对话框上发送一个ok按钮(通过在输入键上放置一些东西并去喝茶)(创建和销毁对话框的简单循环) )程序的内存使用量增加。

TL; DR 简单地创建和销毁gtk窗口(和对话框)似乎会导致内存泄漏。将应用程序保留在循环中会使其内存使用量增加约1900%(从10mb到200mb)。

不,我没有使用为其设计的应用程序测试内存泄漏。 是的,我设置了G_SLICE = always-malloc。 是的,在程序的后台运行另一个线程(但我确定它不会导致任何泄漏) 如果您想了解有关内存中发生的更多信息,我可以从Windows性能监视器发布屏幕。

问题是 - 它是由我造成的内存泄漏,还是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;
}

1 个答案:

答案 0 :(得分:1)

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

这条线尤其没有意义。我们错过了entry的定义,并且您尝试在同一行(*entry = entryBase;)上执行多项操作。

GList *entryBase = gtk_container_get_children(GTK_CONTAINER(gtk_bin_get_child(GTK_BIN(window)))), *entry = entryBase;

但是泄漏可能是您没有提供代码的函数(settProfExists,newProfile)。所以请提供一个重现问题的最小可编辑示例

至于样式,有几个错误:你按照你对数组的方式遍历列表。了解如何实施GList,您会发现这很愚蠢(提示:查看entry->preventry->next):

   for(size_t i=g_list_length(entry); i>0 && !GTK_IS_ENTRY(entry->data); --i)
        entry = g_list_next(entry);

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

你也不应该手工制作路径:你使用的固定长度数组可能不够长。使用GLib提供的可移植g_build_path(并在使用后释放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);