Einleitung
Wenn es mal wieder Memory-Leaks gibt, einfach die DLL vom MemoryLeakIndicator zum Projekt linken.
In der Main bindet man MliForMain.h ein, in den Implementierungsdateien, in denen was mit new instantiiert wird, hinter allen anderen Includes nur die MilDebugNew.h.
Im jeweiligen Projekt muss die MilNewDelete.cpp mit kompiliert werden.
Motivation
Ich nutze mit Visual Studio unter Windows die crtdbg.h um Memory-Leaks angezeigt zu bekommen.
- Siehe auch
- https://docs.microsoft.com/de-de/visualstudio/debugger/finding-memory-leaks-using-the-crt-library?view=vs-2022
Leider werden der Dateiname und die Zeilennummer nur bei Benutzung von malloc, aber nicht bei Nutzung von new in der Ausgabe angezeigt. Bei Memory-Leaks verursacht durch ein new, werden nur die Adresse und die Daten angezeigt. Es ist zwar möglich, den Wertes aus den geschweiften Klammern der Variable _crtBreakAlloc zuzuweisen, in der Hoffnung, dass das Programm bei der Ausführung an der Stelle anhält, wo das nicht gelöschte Objekt instantiiert wird. Aber dieses ist leider nicht immer (z.B. bei DLLs) zuverlässig, da ggf. immer ein anderer Speicherort genommen wird.
Das Finden der verursachenden Anweisungen im Sourcecode gestaltet sich bisweilen schwierig. Gerne hätte hätte ich den Dateinamen und die Zeilennummer als Hinweis.
Implementierung
Wenn es mal wieder Memory-Leaks gibt, einfach die DLL vom MemoryLeakIndicator zum Projekt linken.
Zusätzlich muss im Projekt die Quellatei MilNewDelete.cpp (mit den überladenen Operator new und delete) mit kompiliert werden.
Das gilt auch für selbsterstellte DLLs, wenn dort Memory-Leaks erkannt werden sollen.
In der Main bindet man MliForMain.h ein, in den Implementierungsdateien, in denen was mit new instantiiert wird, hinter allen anderen Includes nur die MilDebugNew.h. Dieses umschließe ich mit einer Präprozessor-Anweisung wie z.B.:
#if defined _MSC_VER && defined _DEBUG_MEMORY_SPY
...
#endif
- Zu beachten
- Es ist nicht von Erfolg gekrönt, alle Sourcen von MemoryLeakIndicator in das eigene Projekt zu übernehmen, also die DLL nicht zu benutzen.
Beispiel
Im folgenden ein kleines Beispiel:
1 #if defined _MSC_VER && defined _DEBUG
2
3 #define _CRTDBG_MAP_ALLOC
4 #include <crtdbg.h>
5 #endif
6
7 #include <iostream>
8 #include <string>
9 #include <conio.h>
10 #if defined _MSC_VER && defined _DEBUG_MEMORY_SPY
11 #include <MliForMain.h>
12 #endif
13
14 class MyClass
15 {
16 private:
17 int * m_piT1;
18 public:
19 MyClass( int i )
20 {
21 m_piT1 = new int (i);
22 };
23 ~MyClass()
24 {
25
26
27 };
28 };
29
30 int main(int argc, char *argv[])
31 {
32 #if defined _MSC_VER && defined _DEBUG
33
34 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
35
36
37
38 #endif
39
40 #if defined _MSC_VER && defined _DEBUG_MEMORY_SPY
42 #endif
43
44
45 MyClass * t_pClass = new MyClass( 1 );
46
47
48 int * t_piLeak = new int( 2 );
49
50
51 char * t_szLeak = new char[100];
52
53
54 #if defined _MSC_VER && defined _DEBUG_MEMORY_SPY
55
57 #endif
58 std::cout << "press any key . . .";
59 _getch();
60 return 0;
61 }
62
const TiOutput OUTPUT_TRACE
Definition MemoryLeakIndicator.h:182
void __mem_leak_indicator_export_dll finishMemoryMonitoring(MemSpy::TiOutput iOutput=MemSpy::OUTPUT_CONSOLE)
const TiOutput OUTPUT_CONSOLE
Definition MemoryLeakIndicator.h:169
__mem_leak_indicator_export_dll MemSpy::LeakMap * activateMemoryMonitoring(MemSpy::TbExceptionMode bExceptionMode=MemSpy::THROW)
Da der Funktion MemSpy::finishMemoryMonitoring(...) die Parameter MemSpy::OUTPUT_TRACE und MemSpy::OUTPUT_CONSOLE übergeben wurden, werden am Ende des Beispielprogramms die Memory-Leaks in der Konsole und im VisualStudio Ausgabefenster angezeigt.
Der Thread 15572 hat mit Code 0 (0x0) geendet.
Memory leaks detected! *
Memory leak in 1313691209200, file D:\Projekte\C++\Src\MemoryLeakIndicator\source\main.cpp, line 45
Memory leak in 1313691165808, file D:\Projekte\C++\Src\MemoryLeakIndicator\source\main.cpp, line 21
Memory leak in 1313691166000, file D:\Projekte\C++\Src\MemoryLeakIndicator\source\main.cpp, line 48
Memory leak in 1313691152224, file D:\Projekte\C++\Src\MemoryLeakIndicator\source\main.cpp, line 51
"MemoryLeakIndicatorDllTestd.exe" (Win32): "C:\Windows\System32\kernel.appcore.dll" geladen.
"MemoryLeakIndicatorDllTestd.exe" (Win32): "C:\Windows\System32\msvcrt.dll" geladen.
"MemoryLeakIndicatorDllTestd.exe" (Win32): "C:\Windows\System32\rpcrt4.dll" geladen.
Der Thread 10624 hat mit Code 0 (0x0) geendet.
Der Thread 17076 hat mit Code 0 (0x0) geendet.
Detected memory leaks!
Dumping objects ->
{172} normal block at 0x00000131DE182F60, 100 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
{171} normal block at 0x00000131DE186530, 4 bytes long.
Data: < > 02 00 00 00
{170} normal block at 0x00000131DE186470, 4 bytes long.
Data: < > 01 00 00 00
{169} normal block at 0x00000131DE190DF0, 8 bytes long.
Data: <pd 1 > 70 64 18 DE 31 01 00 00
Object dump complete.
Das Programm "[15928] MemoryLeakIndicatorDllTestd.exe" wurde mit Code 0 (0x0) beendet.
- Zu beachten
- Wenn man in modernen C++ Projekten Objekte erzeugt und ggf. weiterreicht, verwendet man möglichst keine einfachen Zeiger, sonder Smart-Pointer wie std::unique_ptr oder std::shared_ptr. Es sollte sich i.d.R. nach den Vorgaben von RAII gerichtet werden.
- Siehe auch
- https://docs.microsoft.com/de-de/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170
https://docs.microsoft.com/de-de/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=msvc-170
Einschränkungen
Mir gelang es nicht, den new (std::nothrow) zu überladen. Wenn es sich um eigenen Code handelt, muss dieser new operator entweder für diesen Zweck geändert werden (ohne std::nothrow), oder es wird MilDebugNew.h nicht eingebunden.
Ab und an, gerade in Multithread-Anwendungen, werden vom MemoryLeakIndicator unechte Memory-Leaks angezeigt. Diese sind erkennbar, wenn man die Anwendung im Debug-Modus unter Verwendung von crtdbg.h laufen lässt und in der Ausgabe im VisualStudio Ausgabefenster keine zusätzlich Ausgabe gibt wie z.B.:
…
Detected memory leaks!
Dumping objects →
{172} normal block at 0x00000131DE182F60, 100 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
...
Versuche mit MinGW und GCC sind bei mir auch fehlgeschlagen; geht also nur mit VisualStudio C++. Wenn jemand bereit ist, MemoryLeakIndicator an andere Compiler anzupassen, würde ich mich freuen.