2017-02-08 58 views
0

我編寫了一個簡單的程序模塊來詢問用戶配置文件名稱。爲此,我使用條目窗口小部件創建窗口,並在網格中組織兩個按鈕(確定和取消)。當用戶輸入一個已經存在的配置文件名稱時,通過創建對話框「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

「調試我的代碼爲我」是不是一個好問題。從你的代碼中消除多餘的細節開始,直到你達到[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)功能。所以請提供一個最小的可編譯示例,重現問題

至於風格,有幾個錯誤:你正在用一個數組遍歷列表。瞭解如何實現鏈表本身,你會看到,這是愚蠢的(提示:看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猜測哪裏是小部件,只需創建一個結構,並在回調的gpointer數據參數的結構中傳遞給您感興趣的參數call和GtkEntry。

您也不應該手工製作路徑:您使用的固定長度數組可能不夠長。使用由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); 
+0

呃...我不是一個搜索迷,直到NULL發生,但我認爲它會釋放一個寄存器,所以gcc會有更好的優化可能性。另外,爲什麼要檢查GUI我知道的東西是不好的?我的意思是,在創建一個僅用於顯示一個永遠不會在程序中再次使用的輸入對話框的結構中是否有很多意義? 至於'g_build_path' ...現在有點太晚了。我最近意識到了GLib的威力,並且一旦我開始重構項目就會記住它。是的,我可以摧毀小部件,而不是連接到信號,謝謝。 – Grabusz

+0

這不僅是一個優化問題。您的代碼不應該依賴於UI的組織方式,因此您可以移動小部件並在不破壞代碼的情況下改善用戶體驗。 – liberforce

+0

至於優化,你的例子沒有規模:在一個有100個元素的UI上,遍歷它仍然有意義?不,你不應該試圖反思UI,除非你真的有充分的理由這麼做(比如寫一個像gtk-inspector這樣的內省工具)。 「過早優化是所有邪惡的根源」。只要調用'g_list_length'遍歷整個*列表,然後在循環中第二次遍歷它。除非你確定你的算法是最優的,否則不要在註冊級別考慮。 – liberforce