c - Dialog oprettelse og destruktion loop øger hukommelsen forbrug

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg skrev et simpelt programmodul til at spørge en bruger til et profilnavn. For at gøre det skaber jeg windoww med entry widget, og to knapper (Ok og Cancel) organiseret i et gitter. Når brugeren indtaster et allerede eksisterende profilnavn, informerer den ham om det ved at oprette dialog med 'ok' -knappen, og efter at han trykker på den, går den tilbage til at vælge et profilnavn (vinduet blev ikke skjult eller ødelagt i mellemtiden) . Problemet er, at når jeg opretter en profil, og så spam den ok-knap (ved at placere noget tungt på enter-tasten og tænke at lave te) på både profilnavn vælgeren og dialogen (gør en enkel loop med oprettelse og ødelæggelse af en dialog ) hukommelsesforbruget af programmet øges.





TL; DR
Det er simpelthen at skabe og ødelægge gtk vindue (og dialog), der forårsager en hukommelselækage. Forladelse af app i en loop gjorde det øge dets hukommelsesforbrug med ~ 1900\% (fra 10mb til 200mb).


Nej, jeg testede ikke for hukommelseslækage ved hjælp af apps designet til det.
Ja, jeg har sat G\_SLICE=always-malloc.
Ja, der er en anden tråd, der kører i baggrunden af ​​programmet (men jeg er sikker på, at det ikke forårsager lækager)
Jeg kan sende skærmbilleder fra Windows Performance Monitor, hvis du vil have mere information om, hvad der sker i hukommelsen.


Spørgsmålet er - er det en hukommelseslækage forårsaget af mig, eller er det GTKs fejl (jeg hørte det har en doven politik for hukommelsesstyring, men efter lidt tidshukommelse forbliver brugen fra 200mb til 140mb og forbliver der)?


Her er koden:


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

Bedste reference


Din kode er for ufuldstændig til at finde årsagen til.


Denne linje er specielt ikke fornuftig. Vi mangler definitionen på entry, og du forsøger at gøre flere operationer på samme linje (*entry = entryBase;).


GList *entryBase = gtk\_container\_get\_children(GTK\_CONTAINER(gtk\_bin\_get\_child(GTK\_BIN(window)))), *entry = entryBase;


Lækage kan dog være i funktioner, du ringer til, som du ikke giver koden (setProfExists, newProfile). Så Giv venligst et minimalt kompileret eksempel, der gengiver problemet .


Hvad angår stilen, er der flere fejl: du krydser en liste som du gør med en matrix. Se hvordan implementeret GList, og du vil se, at dette er dumt (hint: se på entry->prev og entry->next):


   for(size\_t i=g\_list\_length(entry); i>0 && !GTK\_IS\_ENTRY(entry->data); --i)
        entry = g\_list\_next(entry);


Derefter et arkitektonisk problem: Prøv ikke at inspicere dit brugergrænseflade for at gætte, hvor er widgetsne, bare opret en struktur, og send dig call parameteren og GtkEntry du er interesseret i i en struktur med gpointer data argument for tilbagekaldelsen.


Du bør også ikke lave stier for hånden: Du bruger en fast længde array, der måske ikke er lang nok. Brug den bærbare g\_build\_path, der leveres af GLib (og frigør stien med g\_free efter brug) , der vil håndtere for dig Windows/Linux kataloger separatorer.


For din dialog, som du kører gtk\_run\_dialog, kan du bare direkte ringe gtk\_destroy\_widget bagefter i stedet for at forbinde et signal:


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