Memory Management
Memory Management
Memory Management
Most of the time spent debugging C++ programs will be in
finding and correcting memory bugs
Can be very difficult to find
The bug may not manifest itself until much later in the code
Invalid Pointers
Uninitialized pointers
Dangling pointers
a = malloc(80);
Malloc example
The Heap
a b
a = malloc(80);
b = malloc(5);
Malloc example
The Heap
a b c
a = malloc(80);
b = malloc(5);
c = malloc(500);
Malloc example
The Heap
a b c d
a = malloc(80);
b = malloc(5);
c = malloc(500);
d = malloc(20);
Free
Used to deallocate memory
Sets the bit in the header to indicate that the
memory is available
The deallocated memory can then be allocated to
another malloc call
Free example
The Heap
a b c d
free(b);
Free
You don’t need to tell the system how much to free –
it gets it from the header
When memory is freed, it is placed on a list of
available memory
free creates fragmented memory
Can reclaim the fragmented memory by looking at
the headers and coalescing contiguous memory that
is not currently allocated
Allocating memory
After several malloc’s and free’s, how should the
system allocate memory
The open memory on the end of the heap
constructor
delete – calls the destructor for each object being
destructed, then calls free
Do NOT use free in C++ - it bypasses the
destructor
If you have allocated an array and call delete
without the [], only the first object has its destructor
called
Debugging Memory Problems
Can be VERY time consuming
VERY difficult to find at times
Three types of memory errors
Memory leak
Dangling pointer
Throwing exceptions
g++ -Wall …
Invalid Pointers
Symptoms of invalid pointers
Data mysteriously changes value for no apparent
reason
Heap data structures become corrupted
a,b
c d
Program crashes when you call new or delete because the heap
is corrupted
occurred
Must be reproducible
Debugging Memory Errors
Step Two: Pinpoint where the error manifests itself
Debugger stack trace
Trace statements
Debugging Memory Errors
Step Three: Shrink the test data
Incrementally delete test data to get it as small as
manifest itself
Look at the code that was most recently
commented out
Debugging Memory Errors
Step Five: Determine exactly where the error was
caused
Tools for finding memory errors
Debugging versions of new/delete
VC++ demo
Debugger watchpoints
Homegrown tools
Memory Watcher
When you allocate memory and initialize it, make
copies of the memory's address and contents
At various points in your code, check to see if the
memory location still has its original value (if it
doesn't, somebody stomped on it)
class MemoryWatcher {
…
public:
void Watch(void * addr, int bytes);
void ReleaseWatch(void * addr);
void Check();
};
Memory Watcher example
MemoryWatcher mw;
…
int *i = new int(15);
mw.Watch(i, sizeof(int));
…
mw.Check();
Memory Allocation Tracker
An alternative method to the memory watcher is a
memory allocation tracker
Write a class that keeps track of all allocated memory
Notify MemoryTracker object whenever memory is
allocated or freed
class MemoryTracker {
…
public:
void Allocated(void * addr, int bytes, string where);
void Freed(void * addr, string where);
void PrintMemoryInfo();
};
Memory Allocation Tracker
When MemoryTracker::Freed is called
If the address was not allocated, generate an
error message
If the address was already deallocated, generate a
Destructor
Operator =
Trees of Pointers
If you have a data structure that has pointers which
point to other pointers, which in turn point to other
pointers, etc. – how do you ensure that things get
deleted corrctly?
By using delete, the entire structure gets deleted
The parent destructor calls the children
destructors, etc.
What about DAGs?
Reference Counting
Oftentimes a single piece of data is pointed to by
multiple pointers
One problem is knowing when to delete the object,
and who should delete it.
The object must not be deleted until everyone
counter is decremented
When the counter hits zero, the object is deleted
Reference Counting
Example:
void SomeFunction(MyObject * x) {
x->AddRef(); // the system copies the pointer
// x is used in the method
. . .
// as x is about to go out of scope, we
// need to decrement the reference count
// If the object's new reference count
// becomes zero, we delete the object.
if (x->ReleaseRef() == 0) {
delete x;
}
}
Reference Counting
Forcing the user to remember when objects are copied
by the system is too error prone
To avoid this, “Smart Pointers” can be used
A smart pointer is a C++ object that
stores a regular pointer to the reference counted
object
automatically keeps track of the number of
references to it
can be used like a normal C++ pointer
->, etc.).