Memory debbuging, templates & overloading operator new

Started by
5 comments, last by emeyex 15 years ago
I've implemented a simple memory manager (actually, it's more of a memory debugger) for one of my projects. All it does is overload operator new and add some header data to every memory allocation on the heap. This info is used to get memory statistics and locate memory leaks. Operator new is overloaded on a global basis to add allocations to a default heap, but can also be overloaded per class in order to allocate objects to specific heaps (e.g. a heap for geometry objects). However, I'm not happy with my current method for overloading operator new for desired classes. At present I'm using macro defines, which can be added to any desired class:

#define MM_DECLARE_HEAP
 public:                                                                   
   static void* operator new(std::size_t uiSize) throw (std::bad_alloc);
private:       
   static Memory::Heap* s_pClassHeap;
   
#define MM_DEFINE_HEAP(className,heapName)  
Memory::Heap* className::s_pClassHeap = 0;                                                
void* className::operator new(std::size_t uiSize) throw (std::bad_alloc)  
{
     if(s_pClassHeap == 0) s_pClassHeap = Memory::HeapFactory::CreateHeap(heapName);
   
   return ::operator new(uiSize, s_pClassHeap);
}   




This feels rather clumsy though, having to add MM_DECLARE_HEAP to any class header and MM_DEFINE_HEAP to the source of any class I want to monitor. The only advantage I can see in this method is that it's easy to disable by just defining these macros as empty in release builds. As an alternative I've been experimenting with using templates instead, as follows:

template<class T>
    class MemoryManaged
    {
        public:
            void* operator new(std::size_t uiSize) throw (std::bad_alloc)
            {
                if(s_pClassHeap == 0) s_pClassHeap = HeapFactory::CreateHeap("Default");
                return ::operator new(uiSize, s_pClassHeap);
            }

        private:
            static Heap* s_pClassHeap;

    };

    template<class T> Heap* MemoryManaged<T>::s_pClassHeap = 0;





This feels cleaner, but I'm still having some problems. Firstly, multiple inheritance is a problem - If a parent and child class both inherit from the template then operator new becomes ambiguous. I'm also unsure unsure what's the best way to pass the heapName to the template. This was obviously trivial in the macro MM_DEFINE_HEAP(className,heapName) but appears less so with a template. As I've discovered, I can't pass a string literal but could pass a globally defined string - again, feels a bit dirty. In the code above "Default" is hard coded for now. Anyway, should I even be looking at templates as a solution or am I heading in the wrong direction? Any suggestions would be welcome :) Cheers! Tim [Edited by - tharris997 on March 31, 2009 9:23:04 PM]
Advertisement
You could just not use the operator new at all, and use your own function or macro to replace it.
Quote:Original post by Steadtler
You could just not use the operator new at all, and use your own function or macro to replace it.


That wouldn't really address my problems - I'd be left with the same issues, under a different function name. It'd also make my code harder to maintain and less robust, as any new or external modules would need to take into account the alternate function. I think renaming the function would really just introduce new problems :(

Cheers!
Do you really want to write your own memory manger?

Your single global operator new is insufficient for a memory manager.

There are several signatures of new (new, new[], placement new, nothrow new, nothrow new[]). Similarly there are forms of delete although you can only call two of them directly. You can call delete and delete[], the system can call nothrow versions of delete and delete[] from within failed constructors, and the versions of placement delete should be no-op functions but may do something for your memory tracking.

If you implement one of them you should implement all twelve of them. The language standard requires that certain combinations must be created as groups -- all the scaler forms as a group, and all the array forms as a group.

To further complicate things, there can be non-standard forms of new and delete within a given compiler.




You might be better off using other libraries entirely.

On Windows you can use the CRT debug heap tools. It will:
* Prevent memory from being released so you can see the state when it was freed
* Check the memory state for buffer overruns
* Check for memory leak detection
* Track every allocation by allocation number along with file name and line
* Set breakpoints on specific memory allocations
* Hook in to the memory management functions to review the memory block

I don't know if that would get you the same memory statistics that you are collecting, but I have seen the DumpClient hook do quite a lot of interesting tracking.

[Edited by - frob on March 31, 2009 10:05:19 PM]
Quote:Original post by frob
Do you really want to write your own memory manger?

Your single global operator new is insufficient for a memory manager.

There are several signatures of new (new, new[], placement new, nothrow new, nothrow new[]). Similarly there are forms of delete although you can only call two of them directly. You can call delete and delete[], the system can call nothrow versions of delete and delete[] from within failed constructors, and the versions of placement delete should be no-op functions but may do something for your memory tracking.


As I said, it's not really a memory manager (yet), but yes I do want to write it because that's how I learn :P ...and as for the other flavours of new, I'm aware of them and do have several of them implemented already. I didn't think it was necessary to post the code for every single one though. Plain old operator new should be sufficient to illustrate my queries.

I do appreciate your input, but I'm really looking for advice as to how I should go about what I'm doing rather than whether I should be doing it in the first place. If I wanted just the "best" solution then you're correct, someone else has almost certainly already written it. However, I won't learn anything from just using someone else code :P

Thanks!
To create an inheritance relationship between different MemoryManaged classes, you can do this:
template<typename T>class MemoryParent {};template<class T>class MemoryManaged: public MemoryParent<T> {  ...};


Suppose you have class foo : bar.

template<>class MemoryParent<foo>: public virtual MemoryManaged<bar> {}


and all of a sudden there is an automatic relationship between MemoryManaged<foo> and MemoryManaged<bar>.

You probably want to inherit from MemoryManaged<foo> virtually, so you don't end up with two of them in your inheritance tree.

For your class name, I'd use a traits class. MemoryManaged will then query the traits class for the class name. This has to be either implemented manually, or done via a macro (one per class).
I say use your macro. You can address the issue of it requiring two corresponding "define"/"declare" sections by simply writing the definition of new and the class heap in the header, i.e.,

#define MM_DECLARE_HEAP(heapName)public:   static void* operator new(std::size_t uiSize) throw (std::bad_alloc)   {      static Memory::Heap* s_pClassHeap=0;      if(s_pClassHeap == 0)        s_pClassHeap = Memory::HeapFactory::CreateHeap(heapName);      return ::operator new(uiSize, s_pClassHeap);}(imagine there are slashes at the end of each line for the macro)


Now all you need is MM_DECLARE_HEAP in your class header file. Don't get me wrong, I love templates, but I'm not sure how they're a win in this case - depends on the specifics of your requirements and how involved you want to get (i.e., whether you want traits classes, heaps that can be shared between classes without being the "global" heap, etc).

On a side note, either your global operator delete will need to be able to detect which heap the returned memory came from, or else you should be sure to implement a corresponding class operator delete.

This topic is closed to new replies.

Advertisement