Garbage Collection is the term used to describe an algorithm that frees allocated memory when it is no longer in use. There are lots of such algorithms available, each with its advantages and disadvantages. I will present here a method of doing garbage collection that is useful for handling normal game data. Contents:
Reference CountingThis is not an unknown term, and it's being used in lots of places to handle shared objects with proper release when the object is no longer needed. This is done by attaching a counter to the object, which holds the number of places that hold a pointer to the object. The user code is responsible for calling AddRef() and Release() to increment and decrement the counter. When the counter reaches 0, the object is deleted. Any one who has ever used this method knows that it can be error prone if the AddRef() and Release() functions are not called properly, resulting in either memory leaks or invalid pointers. Later in this article, a way to solve this problem will be presented. To provide the actual reference counting, a base class which handles the counter is created. All this class provides is an addRef() function which increments the countera, a subRef() function which decrements the counter and returns true if the counter is 0.
All objects which we want to include in this allocation scheme, must be instances of a class derived from this base class. It doesn't have to be a direct parent, just an ancestor, so that the object will have the reference counter available. Smart PointersWhat is a pointer? A definition may be a variable which holds the address of some memory location. A more interesting way to define anything in programming is by its attributes and actions. A pointer has the attribute of holding an address, and has several actions: (assumes: int* p)
A smart pointer is a type that provides all of the above requirements, and perhaps does something extra which is useful. In this case, a smart pointer will make sure to call the addRef() subRef() for us, freeing us from worrying. When should the reference counting be changed for an object? When ever someone acquires a pointer to the object, the counter should be incremented. When ever someone lets go of a pointer to the object, the counter should be decremented. The last one to let go of the pointer should also delete the object. Using C++ features, here is a template class for such a smart pointer. It assumes the template parameter provides the reference counting operations.
The constructors store the pointers and call addRef() to increment the counter, due to this new pointer. The destructor cleans up the counter. This is obviously not enough, since the smart pointer can be changed by assignment
Note that the operator first decrements the counter for the old pointer, and then assigns the new pointer and increments its counter. Now several operators to provide the pointer actions, and some useful conversions:
I recommend removing the last operator, to avoid problems. Use it only if absolutely necessary. Here is the implementation of the private part of the class, which modifies the counter:
UsageUsing these pointers means simply to replace the declaration of the pointer:
All the rest of the code can remain the same, as this type provides the same semantics as a regular pointer. Reference counting and STLA lot of times it is needed to hold objects in a data structure, and not worry about deleting them when clearing, or erasing the objects from the structure. This reference counting scheme does all of that for you. All you need to do is use structures of smart pointers, and you're done. Example:
Now if you clear the vector, or erase the pointer, the object will be freed. If you want to erase it without deleting it, because you need the object somewhere, simply get the pointer and hold it before erasing:
Now the pointer was removed, but the object wasn't deleted. To delete it now, simply do:
Multithreaded OperationIn multithreaded programs, it is necessary to synchronize shared objects, to avoid inconsistencies due to scheduling in the middle of an (in/de)crement. This can be done by adding a mutex to each object, and locking it before doing the actual change of counter. Implementation of this is OS specific. OverheadThere are 2 kinds of overhead when using this scheme:
Usually, when correctness is required, this is a small price to pay. Pros-ConsDrawbacks: This scheme cannot be used in data structures where pointer circles are formed (e.g. doubly linked lists). For tree like structures (trees, vectors, singly linked lists) it can be safely used. Most game data is stored in tree-like structures so it is useful for games. Advantages:
Source CodeHere is a link to the source file. Discuss this article in the forums
See Also: © 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
|