Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

Memory Management

For NVSG, efficient memory management is key to optimizing performance and effectively utilizing available memory.

Introduction

Frequent memory allocations and deallocations play a significant role in degrading application performance. Because a default memory manager is general purpose, it cannot provide the best performance. Also, chaotic software systems tend to suffer from memory leaks and premature object deletion. Memory leaks eventually slow down an application, and premature object deletion can crash a system in cases where memory is accessed after it was already deleted.

NVSG uses a memory manager that is highly optimized for dealing with NVSG specific data structures, and addresses problems that are common to standard memory managers and more chaotic software systems. For example, NVSG uses reference counting, which results in a less chaotic software system that avoids memory corruption in terms of either memory leaks or premature deletion. (See also How to Enable Memory Leak Detection in Debug Mode.)

This section explains the memory management process used by NVSG, and how it affects the methods used to create and delete NVSG objects.

Principles of NVSG Memory Management

The IAllocator class provides an interface to the memory manager by overloading the new and delete operators. NVSG classes utilize this interface through inheritance. This design provides a standard way of using new and delete to allocate and deallocate objects from memory.

The other essential design for NVSG memory management is reference counting. Reference counting conveniently eliminates the burden of object ownership and is basically a simple form of garbage collection. NVSG objects inherit from RCObject, which provides the base functionality for reference counting - that is, encapsulation of the reference count methods and manipulations.

The NVSG framework uses reference counting to

Proper Handling of NVSG Objects

The life of an NVSG object is as follows:

  1. A process creates/allocates the object. The reference counter initializes to zero.
  2. One or more process references the object. Each time a process references the object, the reference counter increments.
  3. When a process is finished using the object, it removes its reference to the object. Each time a process "unreferences" the object, the reference counter decrements.
  4. When the reference counter reaches zero, the object is deallocated.

To meet the requirements of a reference counting pattern, the creation, deletion, copying, and assignment of reference counted objects must be properly controlled.

Summary of Restrictions

NVSG objects are subject to certain restrictions with respect to memory management:

GeoNode node;                               // error! NVSG objects can live on heap only!
GeoNode *pNode0 = new GeoNode;              // error! NVSG objects can't be created using \c new
const GeoNode * pNode1= GeoNode::create();  // fine, creating a GeoNode object on heap using the creation function
// ...
delete pNode1;                              // error! explicit deletion of NVSG objects is not permitted

As a consequence, one cannot store NVSG objects in STL containers like vector or map. Of course, pointers to NVSG objects can be stored in STL containers.

Creating Objects

After creating an NVSG object, the reference count of that object is 0. In other words, the object has been allocated and constructed but not yet referenced. To properly reference the object, one must call addRef on that object.

const GeoNode * pNode = GeoNode::create();  // allocate and construct a GeoNode object
                                            // reference count is 0!
pNode->addRef(); // reference the newly created object
                 // reference count is 1!

The reason that the reference count of an NVSG object is 0 after allocation and construction is that, in many cases, code that allocates and constructs the object does not really reference it, but instead passes it to subsequent layers. In that situation, it would result in unnecessary overhead to also manage the object's life time. For example, a loader might construct a GeoNode object and pass it to a Transform object. In this case the Transform code would be the code which actually references the GeoNode object.

const Transform * pTrafo = Transform::create();
pTrafo->addRef(); 
const GeoNode * pNode = GeoNode::create();  // create a GeoNode object
Transform * pNC = beginEdit( pTrafo );
pNC->addChild(pNode);                       // pass the GeoNode object to the Transform
endEdit( pNC );
// ... no need to care about the GeoNode's lifetime

Destroying Objects

A NVSG object is destroyed and deallocated from memory after its reference count reaches 0 - that is, when nobody is using it any longer. For proper tracking of an object's ownership, code that no longer references an object should call removeRef on that object.

const GeoNode * pNode = GeoNode::create();  // allocate and construct a GeoNode object
                                            // reference count is 0!
pNode->addRef(); // reference the newly created object
                 // reference count is 1!
pNode->removeRef(); // unreference the object
                    // reference count reaches 0, 
                    // and hence the object is destroyed and deallocated from memory 

Back to


Generated on Tue Mar 1 13:20:36 2005 for NVSGSDK by NVIDIA