//--- You would need to declare a ?_ARGS(...) for each class #define A_ARGS(p1) p1 #define B_ARGS(p1,p2) p1,p2 #ifdef CHECK_MEMORY_LEAK #define NEW(ptr,classe,parameters) \ ptr=new classe(parameters); \ mem.mynew(__FILE__, __LINE__, ptr, #classe); #else ptr=new classe(parameters); #endif #ifdef CHECK_MEMORY_LEAK #define DELETE(ptr) \ delete ptr; \ mem.mydelete(ptr); #else delete ptr; #endif //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { A *a1, *a2; B *b1; NEW(a1,A,A_ARGS("Matthias")); NEW(a2,A,A_ARGS("Angelika")); NEW(b1,B,B_ARGS("Alain",2024)); DELETE(a1); // forget 'delete a2;' causing a memory leak! return INIT_SUCCEEDED; }Just for fun as I would not used such construction personally. I don't think it's possible to do better in mql.
Hi,
motivated by Alain I've written a new header file "memoryleak.mqh" for checking the EA code for memory leaks in the strategy tester.
In order to use it there are slight changes to do in your application:
1. Include the header file "memoryleak.mqh" into your code at the very beginning
2. Change
descriptor = new class_object (p1,...,pn)
to
NEW_n (descriptor, class_object, p1,...,pn)
3. Change
delete descriptor
to
DELETE (descriptor)
4. Call MEMORY_RESUME at the end of your program (e.g. in OnDeInit)
That is all! Please see the following example:
#include <Utilities/memoryleak.mqh> struct struct_Data { int intvalue; double doublevalue; }; class CA { private: int m_intvalue; double m_doublevalue; public: CA() { Print ("construct CA without parameter"); PrintFormat ("%s", "No Name"); } CA(string n) { Print ("construct CA with one parameter"); PrintFormat ("%s", n); } CA(struct_Data &data) { Print ("construct CA with one struct"); m_intvalue = data.intvalue; m_doublevalue = data.doublevalue; } CA(string n, int birthyear) { Print ("construct CA with two parameters"); PrintFormat ("%s - year %d", n, birthyear); } ~CA() { Print ("destruct CA"); } void ShowData() { PrintFormat ("int = %d, double = %0.3f", m_intvalue, m_doublevalue); } }; void OnStart() { struct_Data data = {1, 5.6}; CA *zero, *one, *two, *structData; NEW_0 (zero, CA); NEW_1 (one, CA, "Matthias"); NEW_2 (two, CA, "Matthias", 1957); NEW_1 (structData, CA, data); structData.ShowData(); // Forget to delete zero and two: //DELETE (zero); DELETE (one); //DELETE (two); DELETE (structData); MEMORY_RESUME; }
If you forget to free the allocated memory you get a warning text during the testing of your EA by the strategy tester. After you have thoroughly checked and corrected all mistakes causing memory leaks you can outcomment line 5 in the header file "memoryleak.mqh" (marked by an arrow):
#ifndef MEMLEAK_HEADER #define MEMLEAK_HEADER // user can define the following or not: -----> #define CHECK_MEMORY_LEAK //---------------------------------------------------- // do not edit from here //---------------------------------------------------- ................
Hi,
motivated by Alain I've written a new header file "memoryleak.mqh" for checking the EA code for memory leaks in the strategy tester.
In order to use it there are slight changes to do in your application:
1. Include the header file "memoryleak.mqh" into your code at the very beginning
2. Change
descriptor = new class_object (p1,...,pn)
to
NEW_n (descriptor, class_object, p1,...,pn)
3. Change
delete descriptor
to
DELETE (descriptor)
4. Call MEMORY_RESUME at the end of your program (e.g. in OnDeInit)
That is all! Please see the following example:
If you forget to free the allocated memory you get a warning text during the testing of your EA by the strategy tester. After you have thoroughly checked and corrected all mistakes causing memory leaks you can outcomment line 5 in the header file "memoryleak.mqh" (marked by an arrow):
Ill share my code for a heap memory tracer.
@Alain Verleyen , actually, it can be done more seemles integrated. Dont know if this can be accounted for "better".
The code is as is, and needs to be understood, its not recommended to use it in production, because it has a very slow search "algorithm", but it does work reliable.
It will brint out a list of object-ids, which can be used to find the objects in a second run, so that you can insert a "DebugBreak()" when this particulat object will be created. - Of course this only works, if the creation of the objects is deterministic. - Meaning, the sequence of ooperations may not change, as the IDs are createn on the go, and therefore, the code must produce the same "results" on the second run.
It is provided as is.
#define new dbg_heap_obj_trace = new #define delete delete dbg_heap_obj_trace - struct __dbg_heap_obj_trace { // Local storage ulong __sn_cnt; int _size; int _del_size; void* v_obj[]; ulong v_obj_id[]; void* deleted_obj[]; ulong deleted_obj_id[]; __dbg_heap_obj_trace() : __sn_cnt (1), _size (NULL), _del_size (NULL) {}; ~__dbg_heap_obj_trace() { printf("Currently not deleted object count: %i", ArraySize(v_obj_id)); ArrayPrint(v_obj_id); }; template <typename V> const ulong identify(V* p_in) { int cnt = NULL; while( (cnt < _size) && (v_obj[cnt] != p_in) ) { cnt++; } if(cnt == _size) { cnt = NULL; while( (cnt < _del_size) && (deleted_obj[cnt] != p_in) ) { cnt++; } if(cnt < _del_size) { return(deleted_obj_id[cnt]); } } else { return(v_obj_id[cnt]); } printf("%s: %s", "Invalid object query", typename(p_in)); return(-1); }; template <typename V> V* operator=(V* p_in) { _size = ArrayResize(v_obj, _size + 1); ArrayResize(v_obj_id, _size); v_obj[_size - 1] = p_in; v_obj_id[_size - 1] = __sn_cnt++; printf("NEW Obj_id: %i", v_obj_id[_size - 1]); return(p_in); } template <typename V> V* operator-(V* p_in) { int cnt = NULL; while( (cnt < _size) && (v_obj[cnt] != p_in) ) { cnt++; } if(cnt < _size) { _del_size = ArrayResize(deleted_obj, _del_size + 1); ArrayResize(deleted_obj_id, _del_size); deleted_obj[_del_size - 1] = v_obj[cnt]; deleted_obj_id[_del_size - 1] = v_obj_id[cnt]; printf("DEL Obj_id: %llu", v_obj_id[cnt]); ArrayRemove(v_obj, cnt, 1); ArrayRemove(v_obj_id, cnt, 1); _size--; return(p_in); } printf("%s", "DEL Obj_id not found!"); return(p_in); } } dbg_heap_obj_trace;
This content would go into an include .mqh file and you would simply include it into your projects. - No changes to your code are required.
To remove the code, simply remove the include file from your project. - Should work seemles, and could be extended with multiple types of queries and other stuff for more user-convenience.
As addendum, here a version, that also reports where the not deleted objects were created.
(Just typed, not tested, or compiled)
#define new dbg_heap_obj_trace._location_recorder(__LINE__, __FILE__) = new #define delete delete dbg_heap_obj_trace - class __dbg_heap_obj_trace { public: // Local storage int __file_ln; string __file_name; ulong __sn_cnt; int _size; int _del_size; void* v_obj[]; ulong v_obj_id[]; int v_obj_line[]; string v_obj_file[]; void* deleted_obj[]; ulong deleted_obj_id[]; __dbg_heap_obj_trace() : __sn_cnt (1), _size (NULL), _del_size (NULL) {}; ~__dbg_heap_obj_trace() { printf("Currently not deleted object count: %i", ArraySize(v_obj_id)); for(int cnt = NULL; (cnt < ArraySize()); cnt++) { printf("Object ID: %llu; created in file %s at line %i", v_obj_id[cnt], v_obj_line[cnt], v_obj_file[cnt]); } }; __dbg_heap_obj_trace* _location_recorder(const int _line, const string _file) { __file_ln = _line; __file_name = _file; return(GetPointer(this)); }; template <typename V> const ulong identify(V* p_in) { int cnt = NULL; while( (cnt < _size) && (v_obj[cnt] != p_in) ) { cnt++; } if(cnt == _size) { cnt = NULL; while( (cnt < _del_size) && (deleted_obj[cnt] != p_in) ) { cnt++; } if(cnt < _del_size) { return(deleted_obj_id[cnt]); } } else { return(v_obj_id[cnt]); } printf("%s: %s", "Invalid object query", typename(p_in)); return(-1); }; template <typename V> V* operator=(V* p_in) { _size = ArrayResize(v_obj, _size + 1); ArrayResize(v_obj_id, _size); ArrayResize(v_obj_line, _size); ArrayResize(v_obj_file, _size); v_obj[_size - 1] = p_in; v_obj_id[_size - 1] = __sn_cnt++; v_obj_line[_size - 1] = __file_ln; v_obj_file[_size - 1] = __file_name; printf("NEW Obj_id: %i", v_obj_id[_size - 1]); return(p_in); } template <typename V> V* operator-(V* p_in) { int cnt = NULL; while( (cnt < _size) && (v_obj[cnt] != p_in) ) { cnt++; } if(cnt < _size) { _del_size = ArrayResize(deleted_obj, _del_size + 1); ArrayResize(deleted_obj_id, _del_size); deleted_obj[_del_size - 1] = v_obj[cnt]; deleted_obj_id[_del_size - 1] = v_obj_id[cnt]; printf("DEL Obj_id: %llu", v_obj_id[cnt]); ArrayRemove(v_obj, cnt, 1); ArrayRemove(v_obj_id, cnt, 1); ArrayRemove(v_obj_line, cnt, 1); ArrayRemove(v_obj_file, cnt, 1); _size--; return(p_in); } printf("%s", "DEL Obj_id not found!"); return(p_in); } } dbg_heap_obj_trace;
As demo, how this could be achieved, without changing the original source code. - For anyone interested in such advanced code-substitutions, take a look at MQLplus Debugger library from CodeBase. - I have made extensive use of such techniques there to perform seemles custom code substitution.
These types of manipulations can and should be considered as dangerous and are disruptive to anyone not familiar with such techniques, as they change the actual code from what you are seeing on screen. - SO always be aware, if you are using such techniques.
As addendum, here a version, that also reports where the not deleted objects were created.
(Just typed, not tested, or compiled)
As demo, how this could be achieved, without changing the original source code. - For anyone interested in such advanced code-substitutions, take a look at MQLplus Debugger library from CodeBase. - I have made extensive use of such techniques there to perform seemles custom code substitution.
These types of manipulations can and should be considered as dangerous and are disruptive to anyone not familiar with such techniques, as they change the actual code from what you are seeing on screen. - SO always be aware, if you are using such techniques.
@Dominik: many thanks for this code. Very cleverly done, you are a genius! There is no need to change/adapt the application code, that is phantastic!!
Based on your code I've written my own code, slightly adapted to my personal habits. Instead of arrays I use an hashmap. I think, this is more suitable for the application.
Have fun!
#include <Generic/HashMap.mqh> #define new dbg_heap_obj_trace.location_recorder(__LINE__, __FILE__) = new #define delete delete dbg_heap_obj_trace - class Cdbg_heap_obj_trace { private: // Local storage CHashMap<string,string> m_map; int m_line; string m_filename; public: Cdbg_heap_obj_trace() { m_map.Clear(); } ~Cdbg_heap_obj_trace() { Print ("-- Resume Memory Leaks --"); string desc[], infos[]; ArrayFree (desc); ArrayFree (infos); m_map.CopyTo (desc,infos); int size=ArraySize(infos); if (size == 0) { Print (" no memory leaks"); } else { Print (StringFormat ("%d undeleted objects left", size)); for (int i=0; i<size; i++) { Print (StringFormat (" - %s", infos[i])); } } Print ("-- End Resume --"); m_map.Clear(); } Cdbg_heap_obj_trace* location_recorder(const int line, const string filename) { m_line = line; m_filename = filename; return(GetPointer(this)); } template <typename V> V* operator=(V* p_in) { string info = StringFormat ("File %s Line %d", m_filename, m_line); if (!m_map.Add (StringFormat("%x",p_in), info)) { Print (StringFormat ("%s ERROR m_map.Add", __FUNCTION__)); } return p_in; } template <typename V> V* operator-(V* p_in) { string desc = StringFormat("%x",p_in); if (!m_map.ContainsKey(desc)) { Print (StringFormat ("ERROR Object %s not found!", desc)); return p_in; } m_map.Remove (desc); return p_in; } } dbg_heap_obj_trace;
@Dominik: many thanks for this code. Very cleverly done, you are a genius! There is no need to change/adapt the application code, that is phantastic!!
Based on your code I've written my own code, slightly adapted to my personal habits. Instead of arrays I use an hashmap. I think, this is more suitable for the application.
Have fun!
Ill share my code for a heap memory tracer.
@Alain Verleyen , actually, it can be done more seemles integrated. Dont know if this can be accounted for "better".
The code is as is, and needs to be understood, its not recommended to use it in production, because it has a very slow search "algorithm", but it does work reliable.
It will brint out a list of object-ids, which can be used to find the objects in a second run, so that you can insert a "DebugBreak()" when this particulat object will be created. - Of course this only works, if the creation of the objects is deterministic. - Meaning, the sequence of ooperations may not change, as the IDs are createn on the go, and therefore, the code must produce the same "results" on the second run.
It is provided as is.
This content would go into an include .mqh file and you would simply include it into your projects. - No changes to your code are required.
To remove the code, simply remove the include file from your project. - Should work seemles, and could be extended with multiple types of queries and other stuff for more user-convenience.

@Dominik: many thanks for this code. Very cleverly done, you are a genius! There is no need to change/adapt the application code, that is phantastic!!
Based on your code I've written my own code, slightly adapted to my personal habits. Instead of arrays I use an hashmap. I think, this is more suitable for the application.
Have fun!

- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
Hi - happy New Year.
In https://www.mql5.com/en/forum/383008#comment_26181711 I wondered why the strategy tester does not report any memory leaks if occurring in the MQL5 program.
Look the following EA where I "forgot" to free the allocated memory:
If I let run this EA with real data (on a demo account) I get what I expect, namely a short report about the memory leak after having removed the EA from the chart:
But if I let run the EA with history data on the strategy tester I cannot find the report about the memory leak. That is a little bit uncomfortable.
I think the earlier an error is discovered the better. Not only errors in the logic of the trading itself but also technical programming errors like memory leaks.
Therefore I have written a short header file defining a class which detects and reports all memory leaks, especially in the strategy tester.
Copy the header file "memoryleak.mqh" shown below into the Include directory and extend the EA in the following way by the bold printed lines:
The output on application to a chart and removal will be the following:
The bold printed part of the ouput can be seen in the logfile of the strategy tester also. That is exactly what I want.
Later after having thoroughly tested your EA you can comment out the first line of the EA.
Of course the addition of three lines after each new and delete statement is boring, but unfortunately I have no better idea. I did not succed in redefinig the new and delete statements by a macro. Perhaps someone of you???
On the other hand the occurrencies of new and delete statements are not so much normally.
Here the header file "memoryleak.mqh":
May be of help for somebody?
Matthias