LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   How to avoid Memory Leakage? (C/C++) (https://www.linuxquestions.org/questions/programming-9/how-to-avoid-memory-leakage-c-c-393228/)

Thinking 12-15-2005 12:37 PM

How to avoid Memory Leakage? (C/C++)
 
hiho@ll

is there a 100% way to avoid such leaks?
if yes, how does it work? any hints would be great
if not, how does the "not 100%" method work?

i know there are some methods to avoid this (reference counting, ...), so how they work with C/C++?

i could do a self-written malloc or free
but the most time i use "new Object()" and STL stuff like vector or string which doesnt really make it necessary to do a malloc by my own

1: any hints (not only some doc, but some code too) would be cool
2: my biggest problem will be, what if my project i'm currently working on, is getting released and some time later strange behaviour is reported (e.g. it needs much memory resource) and i don't know why?
anybody has an idea of "logging" so i can check, "ok, the most allocated object is xyz which has been allocated n times" (i would only need a hint where the leak could be)
3: i know dlopen and now i want to know if such function is available instead of the "new" keyword like this: void* instantiateObject(char *classname,...); /*... is an argument list for the constructor like a list for printf();*/

thx@ll

sirclif 12-15-2005 12:52 PM

i guess one "tip" would be to make sure your write destructor for any classes you write that allocate memory in the constructor.

jonaskoelker 12-15-2005 01:01 PM

What works 100% is manually and correctly freeing allocated memory.

What also works is reference counting, if your object structures don't contain cycles. If they do contain cycles, try to break them up, make some references weak, or encapsulate the cycle in some class and break in the destructor of that class. shared_ptr from the boost library does the reference counting for you.

You may also want to override operator new and count each allocation (and possibly override operator delete--similarly for the array-operators--to see if any object is leaked). Alternatively, put the allocation counting in an allocator class.

hth --Jonas

dmail 12-15-2005 01:28 PM

this is what i use to track any leaks, but there seems to be a problem with including it in header files and only seems to work if include in the .cpp file. dont know why?

anyway I'm some peeps are going to say theres something wrong with this lol.

also it checks if malloc failed, but doesnt do anything about it!

example of how i use it.

main.cpp
Code:

#include <memory_check.h>

int main(int argc, char **argv)
{
    //create the singleton class
    Memory* memory_ptr = Memory::manager();
    //create some memory
    int* a = new int;
    int* b = new int;
    //recover a
    delete a;
    //delete the manager
    delete memory_ptr;
   
    return 0;
}

this should create a memory leak file with the line and file of the new call.



Code:

#define _MEMORY_CHECK_H_

#include <stdlib.h>
#include <malloc.h>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

typedef struct
{
        void* address;
        unsigned int size;
        std::string file;
        int line;
} memory_info;

__inline string cstring2string(char* c_string)
{
        string message;
        for(int i=0; i<=(int)strlen(c_string); i++)
        { message = message  + c_string[i]; }

        return message;
}

class Memory
{
public:
        static Memory* manager()
        {
                static Memory* m_manager = 0;
                if (! m_manager)
                  { m_manager = new(Memory); }
                return m_manager;
        }

        ~Memory(){save_to_file(); }


        void add(unsigned int size, char* file, int line, void* address)
        {
                memory_info info;
                info.size = size;
                info.file = cstring2string(file);
                info.line = line;
                info.address = address;
                ptr_vector.push_back(info);
        }       

        void remove(void* address)
        {
                for(ptr_iter = ptr_vector.begin(); ptr_iter != ptr_vector.end(); ++ptr_iter)
                {
                        if(ptr_iter->address == address)
                        {
                                ptr_vector.erase(ptr_iter);
                                return;
                        }
                }
        }

        void save_to_file()
        {
                char file_name[]="memory_leaks.txt";
                out.open( file_name, std::ios::out /*| ios::app*/ );

                if(!out.is_open())
                        {cout <<"ERROR\ncould not open file " <<file_name <<endl;}
                else
                {
                        for(ptr_iter = ptr_vector.begin(); ptr_iter != ptr_vector.end(); ++ptr_iter)
                        {
                                out <<"address:" <<ptr_iter->address <<" "
                                        <<"size:" <<ptr_iter->size
                                        <<"\n\tfile:" <<ptr_iter->file <<" "
                                        <<"\n\tline:" <<ptr_iter->line <<" " <<"\n";
                        }
                        out <<"\0";
                        out.close();
                }
        }

private:
        std::ofstream out;
        Memory(){;}
        std::vector<memory_info> ptr_vector;
        std::vector<memory_info> ::iterator ptr_iter;
};

inline void __cdecl operator delete(void* ptr)
{
        Memory::manager()->remove(ptr);
        free(ptr);
}

inline void * __cdecl operator new(unsigned int size,char* file, int line)
{
        void *ptr = (void *)malloc(size);
        if(ptr == 0)
        {
                std::cout <<"memory could not be allocated!\n";
        }
                Memory::manager()->add(size,file,line,ptr);
        return ptr;
}



//some macro magic to make the new call give its line and file
#define DEBUG_NEW new(__FILE__, __LINE__)
#define new DEBUG_NEW


#endif //_MEMORY_CHECK_H_


i should add that i use this code to test parts of code and not full apps.

cyent 12-15-2005 01:32 PM

First, a 100% way of preventing memory leak would be equivalent to the halting problem. (ie. Provably impossible)

Secondly the best design principle is to consider carefully who sould own and be responsible for the allocation and deallocation of the data. The Law of Demeter can be a guide in deciding this, so can the notion of object lifetimes. (ie. The owner must live as least as long as any user of the data)

Thirdly, most practically, use valgrind.

It is very very good.

Thinking 12-16-2005 05:02 AM

the idea with overriding operators seems good
but i think i'll have a look @ http://www.hpl.hp.com/personal/Hans_Boehm/gc/

@dmail
thx for your code
i think i'll use some parts for the beginning

thx@ll

llmmix 12-16-2005 10:56 PM

not sure about %100 But,

1.try not to use memory allocation as possible.

2. ask gnome developer who digging 2.14

as you know, that's why .net and java born.

pragmatic programmer
http://www.amazon.com/exec/obidos/tg...1622X?v=glance

knownrider 12-17-2005 04:12 AM

Quote:

Originally Posted by cyent
Thirdly, most practically, use valgrind.

It is very very good.

I agree.
valgrind can usually be downloaded with your gcc/g++ compiler packages.
For example, in Ubuntu, you'd just: apt-get install valgrind

It's a great tool, specifically for finding memory leaks.

clinux_rulz 12-17-2005 09:22 AM

I'm not sure exactly what you are after. You said some documentation or some code would be cool.

I have here some code for automatic reference counting in c++, that works for objects that inherit the Object class, it is far from complete, but it makes a good example. Just remember when you are using reference counting and you have a child class referencing a perent class and that parent class referencing that same child class, make the child class using a weak reference (just a pointer), that way memory leaks will not occur from circular dependancies.

Code:

//////////////////////////////////////////////////////////////////////////////
// $Revision$
// $Id$
//////////////////////////////////////////////////////////////////////////////

#ifndef _OBJECT_H
#define _OBJECT_H

// Everything here is inline, because it is called alot.
class Object {
  public:
    //Object(): mpCount(new int(1)) {}
    //virtual ~Object() { delete mpCount; }

    //virtual void Ref() const { ++*mpCount; }
    //virtual void Unref() const { if (--*mpCount == 0) delete this; }
    //virtual int refCount() const { return *mpCount; }

    Object(): mCount(1) {}
    virtual ~Object() {}
   
    virtual void Ref() const { ++mCount; }
    virtual void Unref() const { if (--mCount == 0) { delete this; } }
    virtual int refCount() const { return mCount; }
   
  private:
    //int* mpCount;
    mutable int mCount;
};

#endif // _OBJECT_H

//////////////////////////////////////////////////////////////////////////////
// $Log$
//////////////////////////////////////////////////////////////////////////////

// vim:ts=2:sw=2:sts=2:et

Code:

//////////////////////////////////////////////////////////////////////////////
// $Id$
// $Revision$
//////////////////////////////////////////////////////////////////////////////

#ifndef _REF_H
#define _REF_H

template <class TObject>
class Ref {
  private:
    TObject* mpObject;
  public:
    Ref(): mpObject(0) {}
    Ref(TObject* pObject): mpObject(0) { *this = pObject; }
    Ref(Ref const& ref): mpObject(0) { *this = ref; }
    Ref(TObject& object): mpObject(0) { *this = object; }
    ~Ref() { if (mpObject != 0) mpObject->Unref(); }
   
    //-------------------------------------------------------------------------
   
    Ref<TObject>& operator=(TObject* pObject)
    {
      if (mpObject != 0) {
        if (mpObject->refCount() == 1) {
          mpObject->Unref();
          mpObject = 0;
        } else {
          mpObject->Unref();
        }
      }
      mpObject = pObject;
      return *this;
    }
   
    //-------------------------------------------------------------------------
   
    Ref<TObject>& operator=(Ref const& ref)
    {
      if (mpObject != 0) {
        if (mpObject->refCount() == 1) {
          mpObject->Unref();
          mpObject = 0;
        } else {
          mpObject->Unref();
        }
      }
      if (ref.mpObject != 0) {
        ref.mpObject->Ref();
      }
      mpObject = ref.mpObject;
      return *this;
    }
   
    //-------------------------------------------------------------------------
   
    Ref<TObject>& operator=(TObject& object)
    {
      if (mpObject != 0) {
        if (mpObject->refCount() == 1) {
          mpObject->Unref();
          mpObject = 0;
        } else {
          mpObject->Unref();
        }
      }
      object.Ref();
      mpObject = &object;
      return *this;
    }
   
    //-------------------------------------------------------------------------
   
    TObject* operator->()
    {
      return mpObject;
    }
   
    //-------------------------------------------------------------------------
   
    TObject const* operator->() const
    {
      return mpObject;
    }
   
    //-------------------------------------------------------------------------
   
    TObject& operator*()
    {
      return *mpObject;
    }
   
    //-------------------------------------------------------------------------
   
    TObject const& operator*() const
    {
      return *mpObject;
    }
   
    //-------------------------------------------------------------------------
   
    bool operator==(TObject const& object) const
    {
      return mpObject == &object;
    }
   
    //-------------------------------------------------------------------------
   
    bool operator==(TObject const* pObject) const
    {
      return mpObject == pObject;
    }
   
    //-------------------------------------------------------------------------
   
    bool operator==(Ref<TObject> const& ref) const
    {
      return mpObject == ref.mpObject;
    }
   
    //-------------------------------------------------------------------------
   
    bool isNotNull() const
    {
      return mpObject != 0;
    }
   
    //-------------------------------------------------------------------------
   
    bool isNull() const
    {
      return mpObject == 0;
    }
   
    //-------------------------------------------------------------------------
   
    void MakeNull() {
      if (mpObject != 0) {
        mpObject->Unref();
        mpObject = 0;
      }
    }
};

#endif // _REF_H

//////////////////////////////////////////////////////////////////////////////
// $Log$
//////////////////////////////////////////////////////////////////////////////

// vim:ts=2:sw=2:sts=2:et

And to use it is simple.

Code:

class A: virtual public Object {
 . . . Some stuff . . .
};

int main()
{
  A B;
  Ref<A> rA = new A;
  Ref<A> rA2 = A;
  Ref<A> rA3 = B;
  . . . Some more stuff . . .
  return 0;
}

There is no need for Unref() or delete in the sample code above, the Ref<T> class template does everything for you through the Object class. Note: B in the above example uses stack space and is not accidently freed, because Ref<T> addeds an extra reference to make sure the reference count does not hit zero.

cyent 12-18-2005 01:31 PM

See my next post for an example of what can go wrong with ref counted pointers.

cyent 12-18-2005 01:31 PM

See my previous post for an example of what can go wrong with ref counted pointers.

clinux_rulz 12-19-2005 10:33 AM

cyent has a point in his/her last two posts, this problem is mainly avoided by using weak references.

class PhysicsSystem {
public:
. . . BLAH BLAH BLAH . . .
private:
list<Ref<PhysicsObject> > mPhysicsObjects;
};

class PhysicsObject {
public:
. . . BLAH BLAH BLAH . . .
private:
PhysicsSystem* mpParent;
};

In the two examples any of the mPhysicsObjects are referenced to a PhysicsObject, where as each of the PhysicsObject's mpParent(s) are weakly referenced back to the PhysicsSystem it belongs too.

The type PhysicsSystem* is a weak reference, because it is a pointer without any reference counting.

The type Ref<PhysicsObject> is a actual reference, that will do reference counting, and hits zero and deletes itself once nobody is using the PhysicsObject that it references anymore.

Sometimes a system can be too complex to correctly implement with a combination of References and Weak References, in which case Garbarge Collection is a ok idea. Garbages Collection is faster than reference counting, because it does not have to update a counter, and because it does not have to free any memory until any criterion to Collect the Garbage has been meet.

Most the time reference counting should be ok, but do not over use it. Only use it in areas which it is helpful, do not use it for everything. If you have an instance of an object that is only used by one piece of code (like a single class), then do not use reference couning. But if you have an object that a heap of classes may contain, and the order of destruction of the classes is not predetermined or can happen in any order, then do use reference counting.

When using reference counting be carefull to make sure there are no circular dependances, and eleminate any circular dependances with a weak reference. E.g. a doubling linked list with references for the nodes (maybe because the nodes can be spliced together in a specialised list, mimicing a tree), then make the nodes have a Reference to the next node and just a Weak reference to the previous node. Then when you are done unreference the first node and what them all blow up like a chain reaction, until you list is freed.

Unfortunatly if you were implementing a circular list you would have a circular dependance still event with the implementation described in the previos paragraph. And in this case you might now want to use Reference Counting or just simply have a flag at the end of the list where the tail joins to the front so you can double unreference.

clinux_rulz 12-19-2005 10:54 AM

If you need a pointer to an object that will automatically free itself, and is only needed by one section or piece of code, then you can use Auto Pointers.

Code:

template <class T>
class AutoPtr {
  public:
    AutoPtr(): mpObject(0) {}
    AutoPtr(T* pObject): mpObject(pObject) {}
    ~AutoPtr()
    {
      if (mpObject != 0) { delete mpObject; }
    }

    AutoPtr& operator=(T* pObject)
    {
      if (mpObject != 0) { delete mpObject; }
      mpObject = pObject;
      return *this;
    }

    // AND implement the other stuff pointers can do, like comparison operators.
    bool operator==(T* pObject) const
    {
      return mpObject == pObjecy;
    }

    bool operator==(AutoPtr<T> const& autoPtr) const
    {
      return mpObject == autoPtr.mObject;
    }

    bool operator!=  . . . BLAH BLAH BLAH . . .

    . . . BLAH BLAH BLAH . . .

  private:
    T* pObject;
};

Auto pointer you can just treat as pointers, plus unlike normal pointers you can still easly free there memory in the point of an exception, almost like a stacky dynamic memory type of thing.

One problem is that if you wanted more that the one copy of this object, you would have to make more exact copies instead of using the same one.

AutoPtr<Crap>* pMyCrap = new Crap;
AutoPtr<Crap>* pACopyOfMyCrap = new Crap(*pMyCrap);

So on the down side it uses more memory, but on the up side no memory leaks.
Sometimes this can be just as bad as stack variables, and your just better of using them.

AutoPtr(s) are good when you do not have the information to create the object until a function is called that supplies the information. Sort of like delayed construction. Like a render function for a 3d model might supply the plugin for the video at render time, but not at the loading time of the 3d model, so the loading of the textures can be delayed until the video plugin can allocate a texture from video memory to give to the 3d model to use for rendering, then in the next render the texture is no longer needed to be allocated.

Thinking 12-21-2005 12:44 AM

that's a hard topic
i had to read it twice to understand ;-)

but how does garbace collection work?
are AutoPtr the only way in C++?

i mean i understand how php or java garbace collection may work
those languages are interpreted so it's more or less easy
but in C/C++ it seems a challenge to do it correctly

clinux_rulz 12-23-2005 06:42 PM

AutoPtrs are not the only way!!!

Garbage collection works too, but it is a fair amount of work. It involves creating a seperate thread on low priority to check the memory once in a while, and you also must overload your new, new[], delete, delete[] operators, and your malloc() and free() functions, if you entend to use them as well.

To create the platform independand thread for the garbage collection you can use SDL_Thread from the SDL library.

When memory is allocated, extra space for a void* should be allocated as well, so that all allocated objects can be linked together in a forward singular link list fasion. Also you should store the location of the head and the tail of the list as static void pointers in the source file for garbage collection. When new memory is allocated it must be linked on the tail, and when the garbage is collected all the memory should be freed following from the head node onwards.

Also add a dummy class with a dummy static object in your garbage collection source to make sure the all the garbage is collected, so that the garbage is collected before your application terminates.

Code:

class _ {
  public:
    _() {}
    _() { Garbage::Collect(); }
} static __;

The actual implementation of all this I do not feel like doing now. Hopefully someone will implement this below my post.


All times are GMT -5. The time now is 07:45 PM.