我写了一个简单的程序模块来询问用户的个人资料名称。为此,我创建了带有条目小部件的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;
}
答案 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->prev
和entry->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);