Memory Leak
-
Abend,
ich habe eine Funktion, die bei mir offenbar ein Memory Leak erzeugt. Folgende Klasse:
struct SoftwareGraphicsConfigs { std::vector<Adapter> adapters; }; // Adapter ist eine große Struktur mit maps, vectors etc. class Dialog { struct GfxConfigs { RS_TYPE rsType; void* configsData; }; Dialog::~Dialog() { if(mHasConfigs) SAFE_DELETE(mConfigs.configsData) } GfxConfigs mConfigs; bool mHasConfigs; void Dialog::setGraphicsConfigs(const GfxConfigs& configs) { mConfigs.rsType = RS_GFX2; mConfigs.configsData = new SoftwareGraphicsConfigs(configs); mHasConfigs = true; }Wenn ich Dialog::setGraphicsConfigs NICHT aufrufe, gibt es kein Mem Leak. Dh irgendwo da muss das Memory Leak enstehen. Der Dtor von Dialog wird aufgerufen, dh der allokierte Speicher sollte freigegeben werden.
SoftwareGraphicsConfigs ist eine struct mit einem Vektor von Adapter und Adapter ist eine recht große Struktur mit maps, vectors von anderen Strukturen. Allerdings hat keine dieser Strukturen Zeiger!
Dieser Code hier erzeugt ein Mem Leak (er sagt mir nach Programmende, dass noch 8 Bytes allokiert sind):
Dialog dialog("Graphics Settings"); dialog.setGraphicsConfigs(SWconfigs); // Kommentiere ich das aus, gibts kein Leak!Wo liegt das Problem?

-
Anmerkung: Es sind deutlich mehr als 8 Bytes Memory Leak:/
-
Was genau ist eigentlich SAFE_DELETE?
Ansonsten sieht der void-Zeiger verdächtig aus - da hat der Compiler es ein wenig schwer herauszufinden, wie er das dahinterliegende Objekt tatsächlich zerstören soll.PS: Auch wenn du es nicht siehst, vector<> und map<> haben intern Zeiger

-
SAFE_DELETE ist nur ein Makro, das delete aufruft:
#define SAFE_DELETE(p) { if(p) { delete (p); (p) = NULL; } }Ich hab mal zum Test im Dtor den void Zeiger explizit auf SoftwareGraphicsConfigs* gecasted und dann delete aufgerufen. Brauchte auch nichts:(
Die Leaks sehen so aus:
Detected memory leaks!
Dumping objects ->
{973} normal block at 0x022EA368, 8 bytes long.
Data: <0 7 > 30 FC 37 00 00 00 00 00
{972} normal block at 0x022EB6A0, 48 bytes long.
Data: <FarmDef (Pre-Al> 46 61 72 6D 44 65 66 20 20 28 50 72 65 2D 41 6C
{962} normal block at 0x022E7490, 52 bytes long.
Data: < t. t. t. > 90 74 2E 02 90 74 2E 02 90 74 2E 02 CD CD CD CD
{961} normal block at 0x022E6E30, 8 bytes long.
Data: < . > D8 A1 2E 02 00 00 00 00
{960} normal block at 0x022EA228, 24 bytes long.
Data: <( . ( . ( . > 28 A2 2E 02 28 A2 2E 02 28 A2 2E 02 CD CD CD CD
{959} normal block at 0x022E7080, 8 bytes long.Das geht dutzende Zeilen so.
Dass Map/Vektor Zeiger intern benutzen ist mir klar. Aber ich habe in all meinen Strukturen nur map/vektor member, KEINE Zeiger auf map/vektor Instanzen im Heap. Dann sollte doch alles aufgeräumt werden?
-
Leaker schrieb:
SAFE_DELETE ist nur ein Makro, das delete aufruft:
#define SAFE_DELETE(p) { if(p) { delete (p); (p) = NULL; } }Das Grauen scheint nicht auszurotten sein. Ist mir vor Jahren schon so begegnet... Nicht nur dass
SAFE_DELETEkomplett sinnlose Anweisungen enthält, sondern das Ganze ist alles andere als "safe", alleine schon weil es ein Makro ist.Wieso verwendest du nicht gleich den richtigen Zeiger statt
void*? Und warum kein normalesdelete? Forderst du im Konstruktor vonSoftwareGraphicsConfigsnoch irgendwo (auch indirekt) Speicher an? Ansonsten, versuch mal hier einen Smart-Pointer einzusetzen.
-
Wenn das Leak in setGraphicsConfigs auftritt, kann eigentlich nur die 2. Zeile mit dem new zum Leak führen. Da das aber im Destruktor freigegeben werden müsste, gibt es eigentlich nur 2 Optionen: 1. delete auf void* is UB -> hast du aber schon gecheckt (solltest du aber trotzdem ändern) oder 2. aus irgendeinem Grund wird mHasConfigs vorher auf false geändert.
Das SAFE_DELETE-Makro ist übrigens unnötig. delete auf Nullzeiger ist definiert als effektlos (das if kann man sich sparen) und das NULL-Setzen des Zeigers muss auch nicht sein, der wird ja nachher eh nicht mehr verwendet (im Allgemeinen sollte man auch auf blindes NULL-Setzen von Zeigern nach dem delete verzichten, weil es nur den Fehler nach hinten schiebt - es darf außer in Außnahmefällen nicht sein, dass delete mehrmals auf den gleichen Zeiger aufgerufen wird).
-
Was macht denn der Konstruktor von SoftwareGraphicsConfigs, insbesondere: wo lässt er die übergebenen Daten?
-
Und wie sieht der Kopierkonstruktor und Zuweisungsoperator aus?
Edit: von GfxConfigs
-
Also, so etwas wie
void* badidea = new std::string("very very very bad idea"); delete badidea;ist ... *trommel-wirbel* ... eine ganz schlechte Idee. Natürlich entsteht dabei ein Speicherleck, da über das delete mit void*-Zeiger kein einziger Destruktor läuft -- auch nicht string::~string --- wobei das string-Objekt sicherlich irgendwo zusätzlichen Speicher für das Speichern der Zeichenkette angefordert hat. Dieser würde über den Destruktor freigegeben. Merken: Mit void* schmeißt Du sämliche Typinformationen über Bord und bist auf Dich allein gestellt. C++ ist eine "zero overhead abstraction"-Sprache. Auf Deutsch: Es wird kein unnötiger Ballast (wie zB unnötige Laufzeit-Typ-Information) mitgeschleppt. Gerade als Anfänger solltest Du so etwas wie void*-Frickeleien vermeiden.
Zweitens: Wie willst Du überhaupt beurteilen, ob Dein Programm ein Speicherleckt hat? Mach Dir mal ein paar Gedanken dazu, ob die Art und Weise, wie Du das ermittelst, überhaupt korrekt sein kann.
-
Wenn du setGraphicsConfigs() zweimal hintereinander aufrufst, kannst du dir ein Speicherleck einfangen.
Ansonsten sieht, wie schon krümelkacker sagte, die Frickelei mit void* böse aus. Dein Destruktor löscht zwar configsData, aber er ruft nicht den Destruktor von SoftwareGraphicsConfigs auf. Das struct hat zwar keine von dir definierten Destruktor, aber (vermutlich) einen Standarddestruktor welche automatisch die Member-Destruktoren aufruft.