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.
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
To meet the requirements of a reference counting pattern, the creation, deletion, copying, and assignment of reference counted objects must be properly controlled.
new
operator.
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.
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
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