Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to track memory allocations in C++ (especially new/delete)

How can I track the memory allocations in C++, especially those done by new/delete. For an object, I can easily override the operator new, but I'm not sure how to globally override all allocations so they go through my custom new/delete. This should be not a big problem, but I'm not sure how this is supposed to be done (#define new MY_NEW?).

As soon as this works, I would assume it's enough to have a map somewhere of pointer/location of the allocation, so I can keep track of all allocations which are currently 'active' and - at the end of the application - check for allocations which have not been freed.

Well, this seems again like something that surely has been done several times at least, so any good library out there (preferably a portable one)?

like image 856
Anteru Avatar asked Jan 13 '09 10:01

Anteru


People also ask

What happens to memory allocated using new if we lose the pointer to it?

Operator new can fail In the above example, if new fails to allocate memory, it will return a null pointer instead of the address of the allocated memory. Note that if you then attempt indirection through this pointer, undefined behavior will result (most likely, your program will crash).

What is dynamic memory allocation explain new and delete?

C++ allows us to allocate the memory of a variable or an array in run time. This is known as dynamic memory allocation. In other programming languages such as Java and Python, the compiler automatically manages the memories allocated to variables. But this is not the case in C++.

What is dynamic memory location with new and delete keywords?

Dynamic memory allocation in C/C++ refers to performing memory allocation manually by a programmer. Dynamically allocated memory is allocated on Heap, and non-static and local variables get memory allocated on Stack (Refer to Memory Layout C Programs for details).

How do you deallocate memory allocated by new?

New and Delete Operator and to deallocate we use the delete operator. delete ptr; The difference compared to malloc() in C Programming Language is that the new operator does two things: Allocate memory (possibly by calling malloc())


1 Answers

I would recommend you to use valgrind for linux. It will catch not freed memory, among other bugs like writing to unallocated memory. Another option is mudflap, which tells you about not freed memory too. Use -fmudflap -lmudflap options with gcc, then start your program with MUDFLAP_OPTIONS=-print-leaks ./my_program.

Here's some very simple code. It's not suitable for sophisticated tracking, but intended to show you how you would do it in principle, if you were to implement it yourself. Something like this (left out stuff calling the registered new_handler and other details).

template<typename T> struct track_alloc : std::allocator<T> {     typedef typename std::allocator<T>::pointer pointer;     typedef typename std::allocator<T>::size_type size_type;      template<typename U>     struct rebind {         typedef track_alloc<U> other;     };      track_alloc() {}      template<typename U>     track_alloc(track_alloc<U> const& u)         :std::allocator<T>(u) {}      pointer allocate(size_type size,                       std::allocator<void>::const_pointer = 0) {         void * p = std::malloc(size * sizeof(T));         if(p == 0) {             throw std::bad_alloc();         }         return static_cast<pointer>(p);     }      void deallocate(pointer p, size_type) {         std::free(p);     } };  typedef std::map< void*, std::size_t, std::less<void*>,                    track_alloc< std::pair<void* const, std::size_t> > > track_type;  struct track_printer {     track_type * track;     track_printer(track_type * track):track(track) {}     ~track_printer() {         track_type::const_iterator it = track->begin();         while(it != track->end()) {             std::cerr << "TRACK: leaked at " << it->first << ", "                       << it->second << " bytes\n";             ++it;         }     } };  track_type * get_map() {     // don't use normal new to avoid infinite recursion.     static track_type * track = new (std::malloc(sizeof *track))          track_type;     static track_printer printer(track);     return track; }  void * operator new(std::size_t size) throw(std::bad_alloc) {     // we are required to return non-null     void * mem = std::malloc(size == 0 ? 1 : size);     if(mem == 0) {         throw std::bad_alloc();     }     (*get_map())[mem] = size;     return mem; }  void operator delete(void * mem) throw() {     if(get_map()->erase(mem) == 0) {         // this indicates a serious bug         std::cerr << "bug: memory at "                    << mem << " wasn't allocated by us\n";     }     std::free(mem); }  int main() {     std::string *s = new std::string;         // will print something like: TRACK: leaked at 0x9564008, 4 bytes } 

We have to use our own allocator for our map, because the standard one will use our overridden operator new, which would result in an infinite recursion.

Make sure if you override operator new, you use the map to register your allocations. Deleting memory allocated by placement forms of new will use that delete operator too, so it can become tricky if some code you don't know has overloaded operator new not using your map, because operator delete will tell you that it wasn't allocated and use std::free to free the memory.

Also note, as Pax pointed out for his solution too, this will only show leaks that are caused by code using our own defined operator new/delete. So if you want to use them, put their declaration in a header, and include it in all files that should be watched.

like image 111
Johannes Schaub - litb Avatar answered Sep 19 '22 09:09

Johannes Schaub - litb