Hallo,
ich habe in Visual Studio 2010 ein Programm in C++ als
Win32-Konsolenanwendung geschrieben. Da das Programm mitlerweile sehr
Umfangreich geworden ist (mehr als 1000Zeilen in mehreren Dateien)
möchte ich nicht den gesamten Code hochladen. Ich bitte hier um
Verständnis, wenn notwendig kann ich später weitere Teile des Codes
hochladen. Das Problem ist, dass ich ein "kleines" Speicherloch habe.
Nach dem Start verbraucht das Programm 3.344K Speicher. Dieser nimmt
dann mit der Zeit zu.
3:00Uhr 3.344K
4:30Uhr 7.400K
12:38Uhr 33.424K
15:06Uhr 41.312K
.
.
.
Leider konnte ich das memory leaking bis jetzt noch nicht ausfindig
machen und such eine Möglichkeit, einen Hinweis zu bekommen, welche
Variable das Problem der Ursache ist.
Hierzu habe ich online folgende Links gefunden.
http://msdn.microsoft.com/en-us/library/x98tx3cf%28v=VS.100%29.aspxhttp://www.david-amador.com/2010/10/tracking-memory-leaks-in-visual-studio/
Dort wird beschrieben, wie ich in Visual Studio 2010 eine Ausgabe
bekomme, die mir einen Hinweis zu dem Fehler gibt. Leider funktioniert
dies bei meinem Programm nicht. Ich hoffe, dass hier jemand ist, der
diese Funktionalität schon erfolgreich angewendet hat und bereit ist mir
zu helfen.
Im Header "stdafx.h" habe ich folgenden Code ergänzt.
1
#define _CRTDBG_MAP_ALLOC
2
#include<stdlib.h>
3
#include<crtdbg.h>
Direkt am Anfang von der main() habe ich folgenden Code ergänzt.
1
#ifdef _DEBUG
2
intflag=_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
3
flag|=_CRTDBG_LEAK_CHECK_DF;// Turn on leak-checking bit
4
_CrtSetDbgFlag(flag);
5
#endif
Ich hätte jetzt gedacht, dass nach dem ordnungsgemäßem Beenden des
Programms eine Ausgabe stattfindet. Leider konnte ich nicht
herrausfinden, wo diese Ausgabe stehen soll (wahrscheinlich im
Ausgabfenster von Visual Studio) und ich konnte auch keine Ausgabe
sehen.
Steht doch bei deine Link:
> After you have enabled the debug heap functions by using> these statements, you can place a call to _CrtDumpMemoryLeaks> before an application exit point to display a memory-leak> report when your application exits:
Machst du das auch?
A. R. schrieb:> Leider konnte ich nicht herrausfinden, wo diese Ausgabe stehen> soll (wahrscheinlich im Ausgabfenster von Visual Studio)
Genau da landet sie. Da steht beim Programmstart, welche DLLs geladen
werden, da werden Ausgaben via TRACE bzw. OutputDebugString angezeigt,
und dort wird beim Beenden des Programmes auch eine Übersicht über
Speicherlecks ausgegeben.
Wenn Du in allen Klassenimplementierungen das hier drinstehen hast:
1
#ifdef _DEBUG
2
#define new DEBUG_NEW
3
#undef THIS_FILE
4
staticcharTHIS_FILE[]=__FILE__;
5
#endif
dann wird in der Übersicht auch gleich die Stelle angezeigt, an der der
betreffende Speicherblock angefordert wurde.
Klaus Wachtler schrieb:> Steht doch bei deine Link:>> After you have enabled the debug heap functions by using>> these statements, you can place a call to _CrtDumpMemoryLeaks>> before an application exit point to display a memory-leak>> report when your application exits:>> Machst du das auch?
NEIN
Ich mache dieses hier:
>If your application has multiple exits, you do not need to manually place a >call
to _CrtDumpMemoryLeaks at every exit point. A call to _CrtSetDbgFlag >at the
beginning of your application will cause an automatic call to >_CrtDumpMemoryLeaks
at each exit point. You must set the two bit fields >shown here:
Wobei ich sagen muss, dass ich momentan noch ein paar Threads beim
beenden des Programms "hart" beende.
Ich benutze zum auffinden von Speicherlecks immer den "Visual Leak
Detector" (VLD).
OpenSource, kostenlos und einfach zu benutzen ;-)
=> "http://vld.codeplex.com/"
@DanDanger,
danke für die Information.
Das Problem war, dass ich ein paar Threads nicht richtig beende sondern
einfach das Programm schließe. Habe das Programm nun so abgeändert, dass
jeder Thread zuerst beendet wird, bevor das Programm geschlossen wird.
Jetzt klappt auch die Ausgabe von allen Speicherleaks.
Danke für die Hilfe.
Rufus Τ. Firefly schrieb:> A. R. schrieb:>> Leider konnte ich nicht herrausfinden, wo diese Ausgabe stehen>> soll (wahrscheinlich im Ausgabfenster von Visual Studio)>> Genau da landet sie. Da steht beim Programmstart, welche DLLs geladen> werden, da werden Ausgaben via TRACE bzw. OutputDebugString angezeigt,> und dort wird beim Beenden des Programmes auch eine Übersicht über> Speicherlecks ausgegeben.>> Wenn Du in allen Klassenimplementierungen das hier drinstehen hast:>>
1
>#ifdef_DEBUG
2
>#definenewDEBUG_NEW
3
>#undefTHIS_FILE
4
>staticcharTHIS_FILE[]=__FILE__;
5
>#endif
6
>
>> dann wird in der Übersicht auch gleich die Stelle angezeigt, an der der> betreffende Speicherblock angefordert wurde.
Könntest du das bitte nocheinmal etwas genauer erklären.
Wenn ich die Zeilen so in "stdafx.h" einfüge bekomme ich mehrere
Fehlermeldungen beim compilieren.
U.A.:
DEBUG_NEW: nicht deklarierter Bezeichner.
Mittlerweile sehen meine Ergänzungen wie folgt aus.
"stdafx.h":
1
#define _CRTDBG_MAP_ALLOC
2
#define _CRTDBG_MAP_ALLOC_NEW
3
#include<stdlib.h>
4
#include<crtdbg.h>
5
6
#ifdef _DEBUG
7
#ifndef DBG_NEW
8
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
9
#define new DBG_NEW
10
#endif
11
#endif // _DEBUG
Zu Beginn von main:
1
#ifdef _DEBUG
2
//int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
3
//flag |= _CRTDBG_LEAK_CHECK_DF; // Turn on leak-checking bit
Es werden alle Speicherleaks angezeigt. Beim Allozieren mit new
erscheint auch direkt ein Link zu der Stelle. Beim allozieren mit malloc
bekomme ich auch eine Nummer mitgeteilt jedoch keinen Link. Lässt sich
ein solcher Ergänzen? Ggf. ist das genau das was du meinst.
A. R. schrieb:>> Wenn Du in allen Klassenimplementierungen das hier drinstehen hast:>>>>
1
>>#ifdef_DEBUG
2
>>#definenewDEBUG_NEW
3
>>#undefTHIS_FILE
4
>>staticcharTHIS_FILE[]=__FILE__;
5
>>#endif
6
>>
>>>> dann wird in der Übersicht auch gleich die Stelle angezeigt, an der der>> betreffende Speicherblock angefordert wurde.>> Könntest du das bitte nocheinmal etwas genauer erklären.> Wenn ich die Zeilen so in "stdafx.h" einfüge bekomme ich mehrere> Fehlermeldungen beim compilieren.> U.A.:> DEBUG_NEW: nicht deklarierter Bezeichner.>> Mittlerweile sehen meine Ergänzungen wie folgt aus.>> "stdafx.h":>
Das ist genau dasselbe. NUr heisst halt das Makro ein wenig anders.
> erscheint auch direkt ein Link zu der Stelle. Beim allozieren mit malloc> bekomme ich auch eine Nummer mitgeteilt jedoch keinen Link.
Wieso hast du in einem C++ Programm mallocs drinnen?
(Schnell den Knoblauch rausholen, das Kruzifix bereitlegen und den
Exorzisten anrufen. Mach dich auf was gefasst, malloc-Dämon!)
A. R. schrieb:> Das Programm ist Historisch gewachsen.> Habe Code übernommen in dem mallocs enthalten sind.
Nur weil ein Programmierer vor Jahren der Meinug war, es wäre gut
malloc() in C++ Code zu benutzen, heißt das ja nicht dass man diesen
Blödsinn in alle Ewigkeit weiterführen muss.
Wenn es freilich Tausende Zeilen sind die man ändern müsste, um
vernünftigen Code zu haben, wird man dafür oft genug die Zeit nicht
haben, und dann bleibt der Code eben wie er ist (meistens schlecht).
Ach, wie gut kenn ich dieses Problem :-(
A. R. schrieb:> Könntest du das bitte nocheinmal etwas genauer erklären.> Wenn ich die Zeilen so in "stdafx.h" einfüge bekomme ich mehrere> Fehlermeldungen beim compilieren.
Das gehört nicht in eine Headerdatei, sondern in jede C++-Datei. Und
zwar nach das #include "stdafx.h", weil es nicht durch präcompilierte
Headerdateien abgedeckt werden soll.
Darum schrieb ich ja auch Implementierung.
Es wäre sehr nett, wenn Ihr mir noch ein paar Fragen beantworten
könntet.
@ Mark Brandis (markbrandis),
die erste Frage sollte sein, warum man in C++ kein malloc/free verwenden
sollte. Ich habe hierzu jedoch einen informativen Link gefunden. Das hat
sich also eigendlich schon erledigt. Aber vielleicht Interessiert das
auch jemand anderen, deswgen der Link:
http://www.codeproject.com/KB/tips/newandmalloc.aspx
@Rufus Τ. Firefly (rufus) (Moderator),
kannst du bitte noch erklären, warum der Code unbedingt in jeder
C++-Datei muss. Ich dachte, dass der Header "stdafx.h" durch das
#include "stdafx.h" beim compilieren vor die C++-Datei gehangen wird. Es
sollte also keinen Unterschied machen, ob der Code in der Headerdatei
oder der C++-Datei steht.
Es wäre auch noch schön zu erfahren, was der Code macht oder zumindest
wo ich nachlesen kann was "THIS_FILE" , "DEBUG_NEW" und "__FILE__" ist.
Beim einbinden kommt die Fehlermeldun welche sich im Anhang befindet.
Ich konnte jetzt fast alle Speicherleaks beseitigen. Lustigerweise waren
alle "kompilzieren" Operationen mit mehrdimensionalen Pointern richtig.
Der Fehler trag beim allozieren eines einfach Strings auf (ich liebe
Flüchtigkeitsfehler). Das ließ sich ganz einfach finden, da bei der
Fehlerausgabe immer der Inhalt des allozierten Speichers drin steht.
Jetzt bleibt nur noch ein Problem und zwar bekomme ich folgenden Text
ausgegeben.
1
Detectedmemoryleaks!
2
Dumpingobjects->
3
{406}normalblockat0x02ED4170,24byteslong.
4
Data:<>00000000000000001E00000000000000
5
{397}normalblockat0x010C5918,24byteslong.
6
Data:<>00000000000000000100000000000000
7
{390}normalblockat0x010C5720,24byteslong.
8
Data:<>00000000000000000100000000000000
9
{383}normalblockat0x010C4EC0,24byteslong.
10
Data:<>00000000000000000100000000000000
11
Objectdumpcomplete.
Jemand eine Idee, wie ich dem Fehler auf die Schliche kommen kann? Die
Ziffern 383, 390, 397 und 406 sind scheinbar eine Nummerierung der
Allozierungen. Leider schwanken diese von Durchlauf zu Durchlauf. Der
Befehlt "_CrtSetBreakAlloc(406);" hilft mir also nicht weiter.
Wenn das in der präcompilierten Headerdatei steht, kann logischerweise
das Macro __FILE__ nicht mehr aufgelöst werden und die Information,
wo die betreffende Speicheranforderung stattgefunden hat, nicht
ermittelt werden.
Solange Du aber statt new malloc verwendest, geht das sowieso nicht.
Würdest Du new verwenden, stünde in der Ausgabe der Typ des
angeforderten Objekts und die Codezeile, an der es angelegt wurde
(Dateiname/Zeilennummer).
Gibt's eigentlich unter Windows immer noch nicht sowas wie valgrind?
Damit muß man diese ganzen Klimmzüge und Verrenkungen gar nicht machen,
und ob nun malloc oder new verwendet wird, ist auch egal.
> die erste Frage sollte sein, warum man in C++ kein> malloc/free verwenden sollte.
Ein Punkt, der mir auch in deinem Link fehlt, ist die Feststellung, dass
man in C++ malloc/free bzw. new/delete oft auch gar nicht (direkt)
braucht, wenn man in C nicht anders kann.
Warum?
Weil du in C++ die STL zur Verfügung hast. D.h. du musst lineare Listen,
dynamisch wachsende Arrays, wachsende Strings, etc. alles nicht selbst
implementieren, sondern du hast die Dinge bereits fix&fertig zur
Verfügung und musst sie nur noch benutzen. Wo man sich in C mit
char InputLine[80];
einen Ast abprogrammiert und doch nie sicher sein kann bzw. horrenden
Aufwand treiben muss, um Benutzereingaben bulletproof zu machen (der zu
Recht gefürchtete Buffer-Overflow lässt grüßen), hat man in C++ eine
std::string InputLine;
und damit diese Sorge erst mal los.
Ganz abgesehen davon, dass dann plötzlich CopyConstruktor, Assignemnt
Operator und Destruktur oft überflüssig werden und damit auch als
Fehlerquelle weg fallen.
Nutze die Macht, Luke! Dazu ist sie da!
Wer C++ so programmiert, wie er C programmiert, hat es nicht besser
verdient.
A. R. schrieb:> Jemand eine Idee, wie ich dem Fehler auf die Schliche kommen kann? Die> Ziffern 383, 390, 397 und 406 sind scheinbar eine Nummerierung der> Allozierungen. Leider schwanken diese von Durchlauf zu Durchlauf. Der> Befehlt "_CrtSetBreakAlloc(406);" hilft mir also nicht weiter.
so groß sind aber die Differenzen in den Allokierungsnummern nicht.
Setz dir den Break auf 383 und geh dann von dort im SingleStep weiter
(oder mach dir einen weiteren Breakpoint auf eine der Crt-Alloc
Funktionen, bei denen das Programm vorbeikommt. Und dann siehst du dir
an, wo die nächsten, sagen wir mal 10 Allokierungen herkommen. Einer von
diesen wird es sein.