Garbage Collection

Scaleform versions 2.2 and below used a simple reference counting mechanism for ActionScript objects. In most cases this is good enough. However, ActionScript allows you to create circular reference situations, where two or more objects have references to each other. This could result in memory leaks, affecting performance. Consider an example of code that will produce a leak unless one of the object references is explicitly disconnected:

Code:

 var o1 = new Object;
 var o2 = new Object;
 o1.a = o2;
 o2.a = o1;

Theoretically, it is possible to rework such code to avoid circular references, or to have a cleanup function that would disconnect objects in the reference cycle. In most situations, clean up functions do not work well, and the issue becomes even worse if ActionScript’s classes and components are in use. Common cases that could result in memory leaks include the use of singletons, as well as the use of standard Flash UI Components.

To resolve the reference cycles, Scaleform 3.0 introduced garbage collection, a highly-optimized cleanup mechanism based on reference counting. If there are no circular references, this mechanism works as a regular reference counting system. However, in the case when circular references are created, the collector frees otherwise unreferenced objects with circular references. For most Flash files there will be a very little, if any, performance impact.

As mentioned above, memory leaks that were caused by circular references prior to Scaleform 3.0 will be eliminated with garbage collection, which is enabled by default. If not required, the garbage collection functionality may be disabled. However, only customers with access to the Scaleform source code can do this, since it requires rebuilding Scaleform.

To disable garbage collection, open the file Include/GFxConfig.h and comment the line with GFX_AS_ENABLE_GC macro, which is un-commented out by default. However, disabling garbage collection is only supported in AS2. AS3 must use garbage collection.

// Enable garbage collection
#define GFX_AS_ENABLE_GC

After commenting the macro, it is necessary to perform a complete Scaleform library rebuild.

Configuring the memory heap to be used with garbage collection

Scaleform 3.0 and higher allows configuring of the memory heap that hosts the garbage collector. Scaleform 3.3 and higher allows heap sharing, and thus garbage collector sharing, among multiple MovieViews.

To create a new heap for a given MovieDef, the following function may be used:

Movie* MovieDef::CreateInstance(const MemoryParams& memParams, bool initFirstFrame = true)

The MemoryParams structure that is expected as the first argument is defined as a structure as follows:

     struct MemoryParams
    {
        MemoryHeap::HeapDesc   Desc;
        float   HeapLimitMultiplier;
        unsigned            MaxCollectionRoots;
        unsigned            FramesBetweenCollections;
    };

Equivalently, a movie view heap may be created in two steps:

MemoryContext* MovieDef::CreateMemoryContext(const char* heapName, const MemoryParams& memParams, 
                                        bool debugHeap ) 

Movie* MovieDef::CreateInstance(MemoryContext* memContext, bool initFirstFrame = true)

The MemoryContext object encapsulates the movie view heap, as well as other heap-specific objects, such as the garbage collector. This second approach of creating the movie view and its heap through a memory context has the added flexibility of specifying the heap name displayed by AMP, whether the heap is to be thread-safe or not, and whether the heap is to be marked as debug and therefore excluded from AMP reports. More importantly, it allows multiple movie views on the same thread to share a single heap, garbage collector, string manager, and text allocator, thus reducing overhead.

As indicated above, MemoryParams::Desc is used to specify general properties of the memory heap that is used for the movie instance specific allocations (for example, ActionScript allocations). To control the heap footprint it is possible to set two parameters: Desc.Limit and HeapLimitMultiplier.

The heap has initial pre-set limit 128K (the so called "dynamic" limit). When this limit is exceeded, a special internal handler is called. This handler has logic to determine whether to try to free up space, or to expand the heap. The heuristic used to make this decision is taken from the Boehm-Demers-Weiser (BDW) garbage collector and memory allocator.

The BDW algorithm is as follows (pseudo-code):

if (allocs since collect >= heap footprint * HeapLimitMultiplier)
    collect
else
    expand(heap footprint + overlimit + heap footprint * HeapLimitMultiplier)

The “collect” stage includes invocation of the ActionScript garbage collector plus some other actions to free memory, such as flushing internal caches.

The default value for HeapLimitMultiplier is 0.25. Thus, Scaleform will perform memory freeing only if footprint of allocated since the last memory collection is greater than 25% (the default value of HeapLimitMultiplier) of the current heap footprint. Otherwise, it will expand the limit up to the requested size plus 25% of the heap footprint.

If the user has specified Desc.Limit, then the above algorithm works the same way up to that specified limit. If the heap limit exceeds the Desc.Limit value, then collection is invoked regardless of the number of allocations since the last collection. The dynamic heap limit is set to the heap footprint after collection plus any extra memory that is required to fulfill the requested allocation.

The second way to control the garbage collector behavior is to specify the MaxCollectionRoots/ FramesBetweenCollections pair. MaxCollectionRoots specifies the number of roots that causes a garbage collector invocation when exceeded. A “root” is any ActionScript object that potentially may form circular references with other ActionScript objects. In general, an ActionScript object is added to the roots array if it was referenced (touched) by ActionScript (for example, “obj.member = 1” touches the object “obj”). The garbage collector is invoked when the number of touched objects exceeds the value specified in MaxCollectionRoots. By default, this parameter is set to 0, indicating that this mechanism is disabled.

FramesBetweenCollections specifies the number of frames after which the garbage collection is forced to be invoked. The term “frame” here means a single Flash frame. This value may help to avoid performance spikes, which may occur if the garbage collector needs to go through a lot of objects. For example, FramesBetweenCollections may be set to 1800 and the garbage collector will be invoked every 60 seconds with a 30 fps Flash frame rate. The FramesBetweenCollections parameter may also be also used to avoid excessive ActionScript memory heap growth, in the case where Desc.Limit is not set. By default, this parameter is set to 0, indicating that this mechanism is disabled.

GFx::Movie::ForceCollectGarbage

virtual void  ForceCollectGarbage() = 0;

This method can be used to force garbage collector execution by the application. It does nothing if garbage collection is off.