【问题标题】:Addressing memory leak in GTK+ / Queue with windows解决 GTK+ / Queue 中的内存泄漏问题
【发布时间】:2013-12-15 09:34:28
【问题描述】:

我在 windows 中尝试了一个 GTK 应用程序,现在刚刚开始学习 GTK。

GTK 版本:

gtk+-bundle_2.24.10-20120208_win32

申请要求:

1.  Multi threaded
2.  Queue based communication
3.  GTK window to update a image data in screen with 5 secs delay

应用功能:

1.  2 thread (producer and other consumer)
    Producer will post a string to queue manager which will form a structure and post the data to the respective queue
2.  Upon posting the data to the queue manager producer thread shall update the respective image on the screen
3.  Consumer will be monitoring the queue and on receiving data consumer will process the data according

The application is running as expected:

我这里面临的问题是内存(根据 windows 资源监视器)

Confused which one is causing the LEAK -- GTK or my queue or ???

    the program starts with 26K memory and on certain time it reaches 190000K memory. So i see memory leak
    request some guidance in handling this issue

这是我正在工作的示例程序流程。

#include <stdio.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <windows.h>

#define sleep(n) g_usleep(1000 * n)

G_LOCK_DEFINE_STATIC (queue_memchunk);
static GMemChunk   *queue_memchunk = NULL;
static GTrashStack *free_queue_nodes = NULL;
GQueue* msgQueueId[7];


#define NORMAL_TEXT     0
#define WARNING_TEXT    1
#define STATUS_TEXT     2
#define NORMAL_FONT     0
#define SMALL_FONT      1
#define NORM_FONT_UNLTD 2
#define MAX_MSG_SIZE        5120
#define TRACK_ONE_SIZE      150
#define TRACK_TWO_SIZE      100
#define TRACK_THREE_SIZE    100
#define FSM_CHANNEL     1



typedef struct
{
    int msgType;
    int msgSize;
    char msgBuf[MAX_MSG_SIZE];
}STRUCT_MSG;

GtkWidget* window;

struct WidgetsAlive
{
    GtkWidget* widPtr;
    int widIndex;
    struct WidgetsAlive* dependants;
    struct WidgetsAlive* next;
};

/* Linked list ptr to the widgets alive */
static struct WidgetsAlive* wAlive = NULL;
static struct WidgetsAlive* depFirstNode = NULL;
typedef struct {
    char* fileName;
    }dispTextPage_struct;

    typedef struct {
        char* data;
         int row;
         int column;
        char textType;
        char fontType;
        }updateTextPage_struct;

int waitForLoop;

void dispInit(int argc, char* argv[]);
void dispInfoPage(char* fileName, int duration);
gboolean dispTextPage_callBack(dispTextPage_struct *params);
int dispTextPage(char* fileName, int duration);


int destroyNotify(void){
    printf("Notfiy called in");
    while (gtk_events_pending())
          {
            g_usleep (1);
            gtk_main_iteration_do(FALSE);
          }
    waitForLoop = 0;
    return 0;

}


gpointer main_callback(gpointer data)
{
    gtk_main();
    return 0;
}

void dispInit(int argc, char* argv[])
{
    printf("Initializing the display library\n");
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_resize(GTK_WINDOW(window), 640, 480);
    gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
    gtk_widget_realize( window );
    gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
    g_thread_create(main_callback, NULL, FALSE, NULL);
}

void dispInfoPage(char* fileName, int duration)
{
    int index;
    gdk_threads_enter();
    destroyWidget(0);
    GtkWidget *image;
    image = gtk_image_new_from_file(fileName);
    gtk_container_add(GTK_CONTAINER(window), image);
    gtk_widget_show(image);
    gtk_widget_show(window);
    gdk_threads_leave();
}

struct WidgetsAlive* getWidgetFromList(int widgetIndex)
{
        struct WidgetsAlive *temp, *prev;
        temp = wAlive;
        prev = NULL;
        if (temp == NULL)
        {
                /* Widget list is empty. Nothing to destroy */
                return NULL;
        }
        else
        {
            while(temp != NULL)
            {
                if(temp->widIndex == widgetIndex)
                {
                        return temp;
                }
                else
                {
                        //printf("Iterating to the next widget in the list, since the index is different\n");
                        prev = temp;
                        temp = temp->next;
                }
             }
        //printf("No widget with index %d found to destroy, Please check the index..\n", index);
        }
    return NULL;
}

int destroyWidget(int index)
{
    GList *children, *iter;
        struct WidgetsAlive *temp, *prev, *next, *depTemp;
        temp = wAlive;
        prev = wAlive;
        gtk_window_resize((GtkWindow*)window, 800, 600);
        children = gtk_container_get_children(GTK_CONTAINER(window));
        for(iter = children; iter != NULL; iter = g_list_next(iter)){
            gtk_container_remove(GTK_CONTAINER(window),GTK_WIDGET(iter->data));
            //printf("Deleting Widget\n");
        }
        g_list_free(iter);
        g_list_free(children);

    gtk_window_resize((GtkWindow*)window, 800, 600);
  /*  if (temp == NULL)
    {
        return;
    }
    else
    {
        if (temp->widIndex == index)
        {
            if (temp->widPtr != NULL)
            {
                if (GTK_IS_WIDGET(temp->widPtr))
                {
                    //gtk_widget_destroy((GtkWidget*)temp->widPtr);
                    printf("Destroying widget Done\n");
                }
            }
            depTemp = depFirstNode;
            next = depFirstNode;
            while (depTemp != NULL)
            {
                depTemp = depTemp->next;
                free(next);
                next = NULL;
                next = depTemp;
            }
            depFirstNode = NULL;
            wAlive = NULL;
            free(temp);
            temp = NULL;
            return;
        }
        else
        {
            while(temp != NULL)
            {
                if(temp->widIndex == index)
                {
                    printf("Found widget match\n");
                    if (temp->widPtr != NULL)
                    {
                        if (GTK_IS_WIDGET(temp->widPtr))
                        {
                            //gtk_widget_destroy((GtkWidget*)temp->widPtr);
                        }
                    }
                    prev->next = temp->next;
                    free(temp);
                    temp = NULL;
                    return;
                }
                else
                {
                    prev = temp;
                    temp = temp->next;
                }
            }
        }
    }*/
}


int addToWidgetsAlive(GtkWidget* inWidget, struct WidgetsAlive* dependants)

{
    /*struct WidgetsAlive *temp, *node;
    node = (struct WidgetsAlive*)malloc(sizeof(struct WidgetsAlive));
    node->widPtr = inWidget;
    node->widIndex = 1;
    if (dependants != NULL)
        node->dependants = dependants;
    else
        node->dependants = NULL;
    node->next = NULL;
    if (wAlive == NULL)
    {
        wAlive = node;
    }
    else
    {
        temp = wAlive;
        while(temp->next != NULL)
        {
            temp = temp->next;
        }
        node->widIndex = temp->widIndex + 1;
        temp->next = node;
    }
    return node->widIndex;*/
    return 0;
}

int dispTextPage(char* fileName, int duration)
{
//dispTextPage_struct *params;
    dispTextPage_struct *params;
    params = (dispTextPage_struct*)malloc(sizeof(dispTextPage_struct));
    params->fileName = fileName;
    /*
    memset(&params2,'\0',sizeof(dispTextPage_struct));
    params2.fileName = fileName;*/
    while(gtk_events_pending()){
        gtk_main_iteration_do(FALSE);
        g_usleep(1000);
    }
    printf("Gidel added \n");
    waitForLoop=1;
    g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,dispTextPage_callBack,params,destroyNotify);
    while(waitForLoop == 1){
        g_usleep(1000);
    }
    free(params);
}


gboolean dispTextPage_callBack(dispTextPage_struct *params)
{
    char* fileName = params -> fileName;
    int index;
    int isJustifyCenter = 0;

    GtkWidget *textv;
    GdkWindow *textv_window;
    GdkPixmap *pixmap;
    GtkTextBuffer* textBuffer;
    GdkColor color;
    printf("\ntextpage :,1\n");
    destroyWidget(0);
    textv = gtk_text_view_new ();
    gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textv), 22);
    gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textv), 20);
    gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(textv),1);
    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textv), GTK_WRAP_CHAR);
    if (isJustifyCenter == 1)
    {
        gtk_text_view_set_justification(GTK_TEXT_VIEW(textv), GTK_JUSTIFY_CENTER);
    }
    else
    {
        gtk_text_view_set_justification(GTK_TEXT_VIEW(textv), GTK_JUSTIFY_LEFT);
    }

    gtk_text_view_set_editable(GTK_TEXT_VIEW(textv), FALSE);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textv), FALSE);

    gtk_container_add(GTK_CONTAINER(window), textv);
    printf("\ntextpage :,3\n");
    textv_window = gtk_text_view_get_window (GTK_TEXT_VIEW (textv),GTK_TEXT_WINDOW_TEXT);
    gdk_color_parse ("#68604d", &color);
    pixmap = gdk_pixmap_create_from_xpm ((GdkDrawable *) textv_window, NULL,&color, fileName);
    printf(" textpage :,4\n");
    gdk_window_set_back_pixmap (textv_window, pixmap, FALSE);
    g_object_unref(pixmap);
    textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textv));
    gtk_text_buffer_create_tag (textBuffer, "bold","foreground", "Black", NULL);
    gtk_text_buffer_create_tag (textBuffer, "Redbold","foreground", "Red",NULL);
    gtk_text_buffer_create_tag (textBuffer, "fontSize","font", "saxmono 20",NULL);
    gtk_text_buffer_create_tag (textBuffer, "smallFontSize", "font", "saxmono 14", NULL);
    gtk_text_buffer_create_tag (textBuffer, "fontweight","weight", 1000,NULL);
    index = addToWidgetsAlive(textv, NULL);
    gtk_widget_show(textv);
    gtk_widget_show(window);
//free(params);
return 0;
}
gboolean updateTextPage_callBack(updateTextPage_struct *params)
{
    char* data = params -> data;
    int row = params -> row;
    int column = params -> column;
    char textType = params -> textType;
    char fontType = params -> fontType;
    int i;
    int j;
    struct WidgetsAlive* textWindowWidget = NULL;
    GtkWidget *tempTextWidgetPtr;
    GtkTextBuffer *textBuffer;
    gchar* tempLineStr;
    char temp[512];

    GtkTextIter endIter;
    GtkTextIter startIter;
    if(fontType == NORMAL_FONT)
    {
        if(row >15 || row < 0)
            row =15;
        if(column >31 || column < 0)
            column =0;
    }
    // If there is no valid string to write, return
    if ((data == NULL) || (strlen(data) == 0)){
        free(params);
        return FALSE;
    }
    textWindowWidget = getWidgetFromList(1);
    if (textWindowWidget != NULL)
    {
        tempTextWidgetPtr = textWindowWidget->widPtr;
    }
    else
    {
        /* Error */
        free(params);
        return FALSE;
    }
    textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tempTextWidgetPtr));
    gtk_text_buffer_get_end_iter (textBuffer, &endIter);
    gtk_text_buffer_get_start_iter (textBuffer, &startIter);
    i = gtk_text_buffer_get_line_count(textBuffer);
    memset(temp, '\0', sizeof(temp));
    if (i < (row+1))
    {
        for (j = i; j < (row + 1); j++)
        {
            strcat(temp, "\n");
        }
        //Move one char forward if you are at the newline char
        while ((gtk_text_iter_get_char(&endIter) != 0))
        {
            gtk_text_iter_forward_char(&endIter);
        }
        gtk_text_buffer_insert(textBuffer,&endIter, temp, -1);
        gtk_text_buffer_get_end_iter (textBuffer, &endIter);
        gtk_text_buffer_get_iter_at_line(textBuffer, &endIter, row);
        memset(temp, '\0', sizeof(temp));
        for (j = 0; j < column+1; j++)
        {
            strcat(temp, " ");
        }
        gtk_text_buffer_insert(textBuffer, &endIter, temp, -1);
    }
    else
    {
        gtk_text_buffer_get_iter_at_line(textBuffer, &startIter, row);
        gtk_text_buffer_get_iter_at_line(textBuffer, &endIter, row);
        gtk_text_view_forward_display_line_end(GTK_TEXT_VIEW(tempTextWidgetPtr), &endIter);
        tempLineStr = gtk_text_buffer_get_text(textBuffer, &startIter, &endIter, FALSE);
        memset(temp, '\0', sizeof(temp));
        i = strlen(tempLineStr);
        if (column > i)
        {
            for (j = 0; j < (column - i); j++)
            {
                strcat(temp, " ");
            }
            gtk_text_buffer_insert(textBuffer,&endIter, temp, -1);
        }
    }
    gtk_text_buffer_get_iter_at_line_offset(textBuffer, &startIter, row, column);
    endIter = startIter;
    i = strlen(data);
    j = 0;
    while (j < i)
    {
        if ((gtk_text_iter_get_char(&endIter) == '\n'))
        {
            gtk_text_iter_backward_char(&endIter);
            break;
        }
        gtk_text_iter_forward_char(&endIter);
        j++;
    }
    if (gtk_text_iter_compare(&endIter, &startIter) == 1) // delete only if enditer is greater than startiter
        gtk_text_buffer_delete( textBuffer, &startIter, &endIter);
    memset(temp, '\0', sizeof(temp));
    j = strlen(data);
    if ( ((j + column) > 32) && (fontType == NORMAL_FONT))
    {
        strncpy(temp, data, (32 - column));
    }
    else
    {
        strncpy(temp, data, j);
    }
    gtk_text_buffer_insert(textBuffer,&startIter, temp, -1);

    gtk_text_buffer_get_end_iter (textBuffer, &endIter);
    gtk_text_buffer_get_start_iter (textBuffer, &startIter);
    if(textType==NORMAL_TEXT)
        gtk_text_buffer_apply_tag_by_name (textBuffer, "bold", &startIter, &endIter);
    else
    {
        gtk_text_buffer_get_iter_at_line_offset(textBuffer, &startIter, row, column);
        gtk_text_buffer_apply_tag_by_name (textBuffer, "Redbold", &startIter, &endIter);
    }
    if((fontType == NORMAL_FONT) || (fontType == NORM_FONT_UNLTD))
    {
        gtk_text_buffer_apply_tag_by_name (textBuffer, "fontSize", &startIter, &endIter);
    }
    else
    {
        gtk_text_buffer_get_iter_at_line_offset(textBuffer, &startIter, row, column);
        gtk_text_buffer_apply_tag_by_name (textBuffer, "smallFontSize", &startIter, &endIter);
    }
    gtk_text_buffer_apply_tag_by_name (textBuffer, "fontweight", &startIter, &endIter);
    gtk_widget_show(tempTextWidgetPtr);
    gtk_widget_show_all(window);
    free(params);
    return FALSE;
}


void updateTextPage(char* data, int row, int column,char textType,char fontType)
{
    updateTextPage_struct *params;
    params = (updateTextPage_struct*)malloc(sizeof(updateTextPage_struct));
    params->data = data;
    params->row = row;
    params->column = column;
    params->textType = textType;
    params->fontType = fontType;
    while(gtk_events_pending())
    {
        usleep(1000);
    }
    waitForLoop=1;
    g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,updateTextPage_callBack,params,destroyNotify);
    while(waitForLoop == 1){
        usleep(1000);
    }
}

void *fsmThread_RunFunction1()
    {

    int atmMsgRetVal;
    static STRUCT_MSG atmRxMsg;
    while(1)
    {
        memset(&atmRxMsg,'\0',sizeof(STRUCT_MSG));
        atmMsgRetVal = PendMessageQ(FSM_CHANNEL,&atmRxMsg);
        if(atmMsgRetVal == 0)
        {

            if(ProcessfsmMessage(atmRxMsg.msgType, (char *)atmRxMsg.msgBuf))
                continue;
        }
        else{
            //sleep(1);
            usleep(10*1000);
        }
    }
    }

int ProcessfsmMessage(int msgType, char *msgBuf)
{
    printf("fsmThread_RunFunction data recieved %s\n",msgBuf);



}

void *fsmThread_RunFunction()
{
    int pollMsgRetVal = -1;
    sleep(5000);
    dispTextPage("icon132.gif",0);
    char data[500] = "FSM POST MESSAGE\n";
    while(1){

        printf("Now Entry for fsmThread_RunFunction\n");
        PostMessageQ(FSM_CHANNEL,1, data,strlen(data));
        dispTextPage("icon165.gif",0);
        sleep(500);
        printf("Now Exit for fsmThread_RunFunction\n");
    }
}

int main(int argc, char *argv[])
{
    GThread *fsmThreadId,*fsmThreadId1;
    GError *error = NULL;
    g_thread_init(NULL);
    //dispInit(argc, argv);

   // sleep(5000);
    CreateMessageQ(FSM_CHANNEL);
    printf("Initializing the display library\n");
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_resize(GTK_WINDOW(window), 640, 480);
    gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
    gtk_widget_realize( window );
    gtk_window_set_decorated(GTK_WINDOW(window), FALSE);


    dispInfoPage("icon165.gif",1);
    sleep(5000);
    printf("Now create ethread ");
    fsmThreadId = g_thread_create(fsmThread_RunFunction,NULL,TRUE,&error);
    if (error) {
      fflush(stderr);
      exit(1);
    }
    fsmThreadId1 = g_thread_create(fsmThread_RunFunction1,NULL,TRUE,&error);
    if (error) {
      fflush(stderr);
      exit(1);
    }

    gtk_main();

    g_thread_join(fsmThreadId);
    sleep(2);

    printf("ENd of main");
    return 0;
}


int PostMessageQ(int channel, int msgType, char *msgBuf, int msgSize)
{
    STRUCT_MSG txMsg;
    memset(&txMsg,'\0',sizeof(STRUCT_MSG));
    txMsg.msgType = msgType;
    txMsg.msgSize = msgSize;
    memcpy(&txMsg.msgBuf[0],&msgBuf[0],msgSize);
    g_queue_push_head (msgQueueId[channel], (char *)&txMsg);
    return 0;
}

int PendMessageQ(int channel,STRUCT_MSG* rxMsgptr)
{
    if (g_queue_is_empty(msgQueueId[channel])){
        return 1;
    }
    STRUCT_MSG* rxMsg = (STRUCT_MSG*) malloc(sizeof(STRUCT_MSG));
    rxMsg = ( STRUCT_MSG*) g_queue_pop_tail (msgQueueId[channel]);
    rxMsgptr->msgType = rxMsg->msgType;
    rxMsgptr->msgSize = rxMsg->msgSize;
    memcpy(&rxMsgptr->msgBuf[0],&rxMsg->msgBuf[0],sizeof(rxMsg->msgBuf));
    free(rxMsg);
    return 0;
}


void CreateMessageQ (int channel)
{
  G_LOCK (queue_memchunk);
  msgQueueId[channel] = g_trash_stack_pop (&free_queue_nodes);

  if (!msgQueueId[channel])
    {
      if (!queue_memchunk)
        queue_memchunk = g_mem_chunk_new ("GLib GQueue chunk",
                                          sizeof (GNode),
                                          sizeof (GNode) * 128,
                                          G_ALLOC_ONLY);
      msgQueueId[channel] = g_chunk_new (GQueue, queue_memchunk);
    }
  G_UNLOCK (queue_memchunk);

  msgQueueId[channel]->head = NULL;
  msgQueueId[channel]->tail = NULL;
  msgQueueId[channel]->length = 0;
}

【问题讨论】:

  • GTk3(至少在 Linux 上)正在使用一些重要的内存来存储启动时所需的一次性资源(如字体、位图、图像......)。如果内存保持在 180Mbytes,我不会惊慌;如果它继续增长,请担心。并使用内存泄漏检测器(如 Linux 上的 valgrind),同时编译所有警告 - 例如 gcc -Wall -g。顺便说一句,Linux 可能是比 Windows 更好的 GTK 开发平台(您可以在 Linux 上开始开发,并在您的软件成熟时移植到 Windows)。
  • @BasileStarynkevitch :: 我一直在工作的代码来自我们现有的一个已有 5 年历史的 linux 应用程序。
  • 然后在 Linux 上进行改进(针对 GTK3),使用valgrind 检查内存泄漏,然后将其移植到 Windows。
  • 在不知道您使用的是哪个版本的 GTK 的情况下进行一些诊断几乎没有意义:它几乎是在水晶球中阅读。

标签: c memory-leaks gtk message-queue


【解决方案1】:

您没有向我们提供您正在使用的 GTK 版本,但这很可能是某些较旧的 GTK 2 版本的已知问题。检查我对这个问题的回答:https://stackoverflow.com/a/12950519/518853

订阅bug 707760 以跟踪捆绑更新。

【讨论】:

  • gtk+-bundle_2.24.10-20120208_win32.如果这个版本有问题,你会在 GTK2 基础中推荐什么是最好的版本
  • 与我指出的答案的 cmets 中所述相同:降级到 2.24.0 (download 2.24.x bundles),或者自己构建最新的 2.24.x >= 2.24.14。
  • 谢谢这指出我发现程序中的一个泄漏除了这个泄漏之外,我的队列机制和程序错误中也有泄漏。 1. 使用了不需要的 GQUEUE 创建 - g_QUEUE_NEW() 2. 和待处理的数据 2. 错过了免费。如果你能指出一些会有所帮助。发布与答案相同。
  • 能否请您指点一下这个问题:stackoverflow.com/questions/20590410/…
【解决方案2】:
  1. 使用了不需要的 GQUEUE 创建,因此将 CreateMessageQ 更改为如下:

    void CreateMessageQ (int channel) { msgQueueId[channel] = g_queue_new(); }

  2. PendMessageQ free 的变化是问题:

    int PendMessageQ(int channel,STRUCT_MSG* rxMsgptr) { while (g_queue_is_empty(msgQueueId[channel])){ g_usleep(10*1000); } char data = (data) g_queue_pop_tail (msgQueueId[channel]); memcpy(&rxMsgptr->msgBuf[0],&data[0],sizeof(data)); 免费(数据); 返回0; }

【讨论】:

    猜你喜欢
    • 2012-08-19
    • 1970-01-01
    • 2010-12-11
    • 2012-05-23
    • 2011-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多