Noch erreichbares Leck von Valgrind erkannt


154

Alle in diesem Block genannten Funktionen sind Bibliotheksfunktionen. Wie kann ich diesen Speicherverlust beheben?

Es ist in der Kategorie " Noch erreichbar " aufgeführt. (Es gibt 4 weitere, die sehr ähnlich sind, aber unterschiedliche Größen haben)

 630 bytes in 1 blocks are still reachable in loss record 5 of 5
    at 0x4004F1B: calloc (vg_replace_malloc.c:418)
    by 0x931CD2: _dl_new_object (dl-object.c:52)
    by 0x92DD36: _dl_map_object_from_fd (dl-load.c:972)
    by 0x92EFB6: _dl_map_object (dl-load.c:2251)
    by 0x939F1B: dl_open_worker (dl-open.c:255)
    by 0x935965: _dl_catch_error (dl-error.c:178)
    by 0x9399C5: _dl_open (dl-open.c:584)
    by 0xA64E31: do_dlopen (dl-libc.c:86)
    by 0x935965: _dl_catch_error (dl-error.c:178)
    by 0xA64FF4: __libc_dlopen_mode (dl-libc.c:47)
    by 0xAE6086: pthread_cancel_init (unwind-forcedunwind.c:53)
    by 0xAE61FC: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)

Catch: Nachdem ich mein Programm ausgeführt hatte, gab es keine Speicherlecks, aber es gab eine zusätzliche Zeile in der Valgrind-Ausgabe, die vorher nicht vorhanden war:

Verwerfen von Syms bei 0x5296fa0-0x52af438 in /lib/libgcc_s-4.4.4-20100630.so.1 aufgrund von munmap ()

Wenn das Leck nicht behoben werden kann, kann jemand zumindest erklären, warum die Zeile munmap () Valgrind veranlasst, 0 "noch erreichbare" Lecks zu melden?

Bearbeiten:

Hier ist ein minimales Testbeispiel:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *runner(void *param) {
    /* some operations ... */
    pthread_exit(NULL);
}

int n;

int main(void) {

    int i;
    pthread_t *threadIdArray;

    n=10; /* for example */

    threadIdArray = malloc((n+n-1)*sizeof(pthread_t));  

    for(i=0;i<(n+n-1);i++) {
        if( pthread_create(&threadIdArray[i],NULL,runner,NULL) != 0 ) {
            printf("Couldn't create thread %d\n",i);
            exit(1);
        }
    }


    for(i=0;i<(n+n-1);i++) {
        pthread_join(threadIdArray[i],NULL);
    }

    free(threadIdArray);

    return(0);
}

Laufen Sie mit:

valgrind -v --leak-check=full --show-reachable=yes ./a.out

Antworten:


378

Es gibt mehr als eine Möglichkeit, "Speicherverlust" zu definieren. Insbesondere gibt es zwei primäre Definitionen von "Speicherverlust", die unter Programmierern gebräuchlich sind.

Die erste häufig verwendete Definition von "Speicherverlust" lautet: "Speicher wurde zugewiesen und wurde anschließend nicht freigegeben, bevor das Programm beendet wurde." Viele Programmierer argumentieren jedoch (zu Recht), dass bestimmte Arten von Speicherlecks, die dieser Definition entsprechen, tatsächlich kein Problem darstellen und daher nicht als echte "Speicherlecks" betrachtet werden sollten.

Eine wohl strengere (und nützlichere) Definition von "Speicherverlust" lautet: "Speicher wurde zugewiesen und kann anschließend nicht freigegeben werden, da das Programm keine Zeiger mehr auf den zugewiesenen Speicherblock hat." Mit anderen Worten, Sie können keinen Speicher freigeben, auf den Sie keine Zeiger mehr haben. Ein solcher Speicher ist daher ein "Speicherverlust". Valgrind verwendet diese strengere Definition des Begriffs "Speicherverlust". Dies ist die Art von Leck, die möglicherweise zu einer erheblichen Erschöpfung des Heapspeichers führen kann, insbesondere bei langlebigen Prozessen.

Die Kategorie "noch erreichbar" im Leckbericht von Valgrind bezieht sich auf Zuordnungen, die nur der ersten Definition von "Speicherverlust" entsprechen. Diese Blöcke wurden nicht freigegeben, aber sie hätten freigegeben werden können (wenn der Programmierer dies gewollt hätte), da das Programm immer noch Zeiger auf diese Speicherblöcke verfolgte.

Im Allgemeinen müssen Sie sich keine Gedanken über "noch erreichbare" Blöcke machen. Sie stellen nicht das Problem dar, das echte Speicherlecks verursachen können. Beispielsweise besteht normalerweise kein Potenzial für eine Erschöpfung des Haufens durch "noch erreichbare" Blöcke. Dies liegt daran, dass es sich bei diesen Blöcken normalerweise um einmalige Zuordnungen handelt, auf die während der gesamten Lebensdauer des Prozesses verwiesen wird. Sie können zwar sicherstellen, dass Ihr Programm den gesamten zugewiesenen Speicher freigibt, dies hat jedoch normalerweise keinen praktischen Vorteil, da das Betriebssystem ohnehin den gesamten Speicher des Prozesses nach Beendigung des Prozesses zurückfordert. Vergleichen Sie dies mit wahr Speicherlecks, die, wenn sie nicht behoben werden, dazu führen können, dass einem Prozess der Speicher ausgeht, wenn sie lange genug ausgeführt werden, oder einfach dazu führen, dass ein Prozess viel mehr Speicher verbraucht als erforderlich.

Wahrscheinlich ist es nur dann sinnvoll sicherzustellen, dass alle Zuordnungen übereinstimmende "Freigaben" haben, wenn Ihre Leckerkennungstools nicht erkennen können, welche Blöcke "noch erreichbar" sind (Valgrind kann dies jedoch) oder wenn Ihr Betriebssystem nicht alle zurückfordert Speicher eines abschließenden Prozesses (alle Plattformen, für die Valgrind portiert wurde).


Kannst du vermuten, was die Munmap () tut, wodurch die "noch erreichbaren" Blöcke verschwinden?

3
@crypto: Möglicherweise munmapwird dies beim Entladen eines gemeinsam genutzten Objekts aufgerufen. Alle vom freigegebenen Objekt verwendeten Ressourcen werden möglicherweise freigegeben, bevor es entladen wird. Dies könnte erklären, warum die "noch erreichbaren" in dem munmapFall befreit werden. Ich spekuliere hier nur. Es gibt hier nicht genug Informationen, um sicher zu sein.
Dan Moulding

3
Ein Fall, in dem "noch erreichbarer" Speicher als Speicherverlust angesehen werden kann: Angenommen, Sie haben eine Hash-Tabelle, in der Sie Zeiger auf den Heap-zugewiesenen Speicher als Wert hinzufügen. Wenn Sie weiterhin neue Einträge in die Tabelle einfügen, aber nicht mehr benötigte Einträge entfernen und freigeben, kann dies unbegrenzt wachsen und ein Heap-Speicherereignis verlieren, wenn dieser Speicher tatsächlich "noch erreichbar" ist. Dies ist der Fall von Speicherverlusten, die in Java oder anderen durch Müll gesammelten Sprachen auftreten können.
lvella

Siehe auch diese Antwort in den Valgrind-FAQ zu "noch erreichbaren" Blöcken, die von STL erstellt wurden. valgrind.org/docs/manual/faq.html#faq.reports
John Perry

5
"Viele Programmierer argumentieren (zu Recht), dass [durchgesickerter Speicher] eigentlich kein Problem darstellt und daher nicht als echte Speicherlecks angesehen werden sollte" - Lol ... Erstellen Sie eine native DLL mit dieser Art von Speicherleck und dann Lassen Sie Java oder .Net es konsumieren. Java und .Net laden und entladen DLLs tausende Male während der Laufzeit eines Programms. Jedes Mal, wenn die DLL neu geladen wird, geht etwas mehr Speicher verloren. Lang laufende Programme haben möglicherweise nicht mehr genügend Speicher. Es macht Debians OpenJDK-Betreuer verrückt. Er sagte dasselbe auf der OpenSSL-Mailingliste, während wir über die "gutartigen" Speicherlecks von OpenSSL diskutierten.
JWW

10

Da es unten eine Routine aus der pthread-Familie gibt (aber ich kenne diese nicht), würde ich vermuten, dass Sie einen Thread als Joinable gestartet haben, der die Ausführung beendet hat.

Die Informationen zum Exit-Status dieses Threads bleiben verfügbar, bis Sie aufrufen pthread_join. Somit wird der Speicher bei Programmbeendigung in einem Verlustprotokoll gespeichert, ist jedoch weiterhin erreichbar, da Sie pthread_joinfür den Zugriff darauf verwenden können.

Wenn diese Analyse korrekt ist, starten Sie diese Threads entweder getrennt oder verbinden Sie sie, bevor Sie Ihr Programm beenden.

Bearbeiten : Ich habe Ihr Beispielprogramm ausgeführt (nach einigen offensichtlichen Korrekturen) und ich habe keine Fehler, aber die folgenden

==18933== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
--18933-- 
--18933-- used_suppression:      2 dl-hack3-cond-1
--18933-- used_suppression:      2 glibc-2.5.x-on-SUSE-10.2-(PPC)-2a

Da das dl-Ding viel von dem ähnelt, was Sie sehen, denke ich, dass Sie ein bekanntes Problem sehen, das eine Lösung in Bezug auf eine Unterdrückungsdatei für hat valgrind. Möglicherweise ist Ihr System nicht auf dem neuesten Stand oder Ihre Distribution verwaltet diese Dinge nicht. (Meins ist Ubuntu 10.4, 64bit)


Ich bekomme genau wie Sie 0 Fehler. Bitte überprüfen Sie die Leckübersicht auf Informationen zu den "Lecks".

@ Crypto: Ich verstehe nicht. Du meinst, du hast die gleichen Unterdrückungen wie ich?
Jens Gustedt

used_suppression: 14 dl-hack3-cond-1 <- das bekomme ich

6

Sie scheinen nicht zu verstehen, was still reachablebedeutet.

Alles still reachableist kein Leck. Sie müssen nichts dagegen tun.


24
Dies steht im Widerspruch zu den anderen von Valgrind bereitgestellten und technisch inkorrekten Worten. Der Speicher war beim Beenden des Programms "noch erreichbar" und somit möglicherweise ein Leck. Was wäre, wenn Sie Code debuggen würden, um ihn unter einem RTOS auszuführen, das den Speicher nach dem Beenden des Programms nicht gut bereinigt?
Toymakerii

4
Das stimmt leider nicht immer. Deskriptoren für verlorene Dateien können beispielsweise als Speicherverlust gelten, aber Valgrind klassifiziert sie als "noch erreichbar", vermutlich weil die zu ihnen führenden Zeiger in einer Systemtabelle weiterhin zugänglich sind. Zum Zwecke des Debuggens ist die eigentliche Diagnose jedoch ein "Speicherverlust".
Cyan

Verlorene Dateideskriptoren sind per Definition keine Speicherlecks. Vielleicht sprechen Sie über verlorene FILEZeiger?
Angestellt Russisch

6

Hier ist eine richtige Erklärung für "noch erreichbar":

"Noch erreichbar" sind Lecks, die globalen und statisch-lokalen Variablen zugewiesen sind. Da valgrind globale und statische Variablen verfolgt, kann es Speicherzuordnungen ausschließen, denen "einmalig und vergessen" zugewiesen wurde. Eine globale Variable hat eine Zuordnung einmal zugewiesen und diese Zuweisung nie neu zugewiesen. Dies ist normalerweise kein "Leck" in dem Sinne, dass sie nicht unbegrenzt wächst. Es ist immer noch ein Leck im engeren Sinne, kann aber normalerweise ignoriert werden, es sei denn, Sie sind pedantisch.

Lokale Variablen, denen Zuweisungen zugewiesen und nicht freigegeben wurden, sind fast immer Lecks.

Hier ist ein Beispiel

int foo(void)
{
    static char *working_buf = NULL;
    char *temp_buf;
    if (!working_buf) {
         working_buf = (char *) malloc(16 * 1024);
    }
    temp_buf = (char *) malloc(5 * 1024);

    ....
    ....
    ....

}

Valgrind meldet working_buf als "noch erreichbar - 16k" und temp_buf als "definitiv verloren - 5k".


-1

Für zukünftige Leser bedeutet "Noch erreichbar" möglicherweise, dass Sie vergessen haben, so etwas wie eine Datei zu schließen. Während es in der ursprünglichen Frage nicht so scheint, sollten Sie immer sicherstellen, dass Sie das getan haben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.