2017-02-08 15 views
0

Я написал простой программный модуль, чтобы спросить у пользователя имя профиля. Для этого я создаю windoww с виджем ввода и двумя кнопками (Ok и Cancel), организованными в сетке. Когда пользователь вводит имя профиля, которое уже существует, он информирует его об этом факте, создавая диалог с кнопкой «ok», и после того, как он нажимает его, он возвращается к выбору имени профиля (окно не было скрыто и не было уничтожено тем временем) , Проблема заключается в том, что когда я создаю профиль, а затем спам кнопки ok (помещая что-то тяжелое на ключ ввода и собираюсь делать чай) как на выбор имени профиля, так и на диалог (создавая простой цикл с созданием и уничтожением диалога) использование памяти программы увеличивается.Цикл создания и уничтожения диалогов увеличивает использование памяти


TL; DR Просто создания и кажется, уничтожив окно GTK (и диалога), чтобы вызвать утечку памяти. Оставляя приложение в цикле, он увеличил использование памяти на ~ 1900% (от 10 до 200 мб).

Нет, я не тестировал утечки памяти с помощью приложений, предназначенных для этого. Да, я установил G_SLICE = always-malloc. Да, в фоновом режиме программы есть другой поток (но я уверен, что он не вызывает никаких утечек) Я могу опубликовать экраны из Windows Performance Monitor, если вы хотите узнать больше о том, что происходит в памяти.

Вопрос - это утечка памяти, вызванная мною, или это ошибка GTK (я слышал, что у нее ленивая политика управления памятью, но через некоторое время использование памяти падает с 200 до 140 мб и остается там)?

Вот код:

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

«Отладка мой код для меня» не хороший вопрос. Начните с устранения лишних деталей из вашего кода, пока не достигнете [mcve]. Вы можете найти проблему самостоятельно по пути. – StoryTeller

+0

Я вижу вашу функцию для создания окна, но я не вижу никакого фактического создания или уничтожения каких-либо окон. –

+0

@JohnBollinger «chooseProfName» здесь только для полноты. Он создает окно, которое постоянно открыто в цикле. Цикл действителен в pickNameButtCB, запускает showError, где диалог создается и уничтожается (я подключил сигнал «ответ» к функции уничтожения виджета). – Grabusz

ответ

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); 

Тогда архитектурная проблема: не пытайтесь проверить ваш пользовательский интерфейс, чтобы угадать, где находятся виджеты , просто создайте структуру и передайте вам параметр call и интересующий вас GtkEntry в структуре с аргументом данных gpointer обратного вызова.

Вы также не должны вводить пути вручную: вы используете массив фиксированной длины, который может быть недостаточно длинным. Используйте портативный g_build_path, предоставленный GLib (и освободите путь с помощью 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); 
+0

Ну ... Я не поклонник поиска до тех пор, пока не произойдет NULL, но я думаю, что он освободит один регистр, поэтому gcc будет иметь лучшие возможности оптимизации. Кроме того, почему проверка GUI на то, что я знаю, есть плохо? Я имею в виду, есть ли смысл в создании структуры исключительно для отображения диалогового окна ввода, которое никогда не будет использоваться снова в программе? Что касается 'g_build_path' ... сейчас немного поздно. Недавно я понял силу GLib и буду помнить об этом, когда начну рефакторинг проекта. И да, я могу просто уничтожить виджет вместо подключения к сигналу, спасибо за это. – Grabusz

+0

Это не только проблема оптимизации. Ваш код не должен опираться на то, как настраивается пользовательский интерфейс, поэтому вы можете перемещать виджеты и улучшать работу пользователей, не нарушая ваш код. – liberforce

+0

Что касается оптимизации, то ваш пример не масштабируется: на пользовательском интерфейсе с 100 элементами в нем, пройдя его, все еще имеет смысл? Нет. Вы не должны пытаться исследовать пользовательский интерфейс, если у вас нет достаточных оснований для этого (например, написать инструмент интроспекции, такой как gtk-inspector). Также «преждевременная оптимизация - это корень всего зла». Просто вызов 'g_list_length' проходит один раз в * полный список, затем вы проходите его второй раз в своем цикле. Не думайте на уровне регистра, если вы не уверены, что ваш алгоритм оптимален. – liberforce

 Смежные вопросы

  • Нет связанных вопросов^_^