Microsoft DirectX 9.0

Garbage Collection

Caching of loaded objects can lead to wasted memory when an application loads many objects, particularly objects that reference other objects.

When automatic caching is enabled, as it is by default, every object loaded by IDirectMusicLoader8::GetObject is cached, including objects that are loaded by reference. For example, if you call GetObject on a segment, and that segment contains a reference to a script, the script is loaded and cached as well.

When you call IDirectMusicLoader8::ReleaseObject or IDirectMusicLoader8::ReleaseObjectByUnknown, however, only the primary object that was loaded by GetObject is removed from the cache. Referenced objects are not released, even if they are not being used by other objects.

In order to clean up objects that are not in use, call IDirectMusicLoader8::CollectGarbage. This method releases all objects from the cache except objects directly loaded by GetObject and objects referenced by them. Objects only referenced by other objects that no longer exist are released. CollectGarbage clears an object from the cache by releasing the loader's COM reference to the object. If the object's reference count drops to zero as a result, the object destroys itself, thus making its memory available again.

In summary, to ensure that loaded objects do not remain in memory when no longer needed, you must do the following:

A complication arises when objects have circular references to one another. Suppose the script track of a segment contains a reference to a script object, and this script object contains a reference to the segment. You load the segment directly by calling GetObject, and the script is loaded indirectly. Then you release the segment from the cache by using ReleaseObject, and call Release on your application's reference to it. The segment continues to exist because there is still one COM reference to it, which is held by the script object. The script is now garbage, because it is not referenced by any other object in the cache. Without taking special measures, however, CollectGarbage could only release the loader's reference to the script; therefore its reference count would not drop to zero. The segment and script would continue to be referenced by one another, and although both were removed from the cache, they would both continue to exist in memory.

To avoid this problem, CollectGarbage calls an internal method on an object that forces the object to release its references to other objects. In the example above, it causes the script to release its reference to the segment. The segment's reference count drops to zero, and in the course of destroying itself, the segment releases its reference to the script, thus allowing the script to destroy itself when the loader releases its reference.

There is one more complication, however. Suppose the application has obtained an interface to the script that the loader knows nothing about, and the application neglects to call Release on this pointer. The script continues to exist, but it might not be able to behave as it should, because it no longer has a reference to the segment. Calling a method on the script could lead to a fatal error. To prevent this, CollectGarbage ensures that all methods of the script object return DMUS_S_GARBAGE_COLLECTED.

This scenario does not affect most applications. However, you should be aware that calling a method on an object that has been cleared from the cache by CollectGarbage might not yield the desired result.

In the following example function, assume that the loaded script contains a reference to a segment. After calling a routine in the script, the function removes the script object from the cache and then calls CollectGarbage, which releases the referenced segment. If the segment contains a circular reference to the script, this is released so that the script can be destroyed, in turn releasing the final reference to the segment and allowing the segment to be destroyed.

void CallWhistle(IDirectMusicLoader8* pLoader, IDirectMusicPerformance8* pPerformance)
{ 
  IDirectMusicScript8 *pScript;
  WCHAR wszScript[MAX_PATH] = L"soundfx.spt";
 
  pLoader->LoadObjectFromFile(CLSID_DirectMusicScript, 
                              IID_IDirectMusicScript8, 
                              wszScript, (void**)&pScript);
  pScript->Init(pPerformance, NULL);
  pScript->CallRoutine(L"Whistle", NULL);
 
  pLoader->ReleaseObjectByUnknown(pScript);
  pLoader->CollectGarbage();
  pScript->Release();
}

See Also