2.2.5 Managing memory

As components may be written in languages that use different memory management strategies, it is impossible for the runtime system of a component model implementation to allocate memory for objects. Component models that use formalized language bindings delegate object instantiation to them, whereas those with no such formalized bindings generally require that components provide factories that instantiate objects. In order to instantiate objects, component model implementations must be able to map runtime names of objects to the components that house them, and to their factories.

Some second-generation component models set standards for managing the lifetime of objects. First-generation component models use explicit lifetime management, meaning that an instance can be explicitly destroyed by any party. This strategy does not work well with a large number of independent clients that are not aware of one another. A client can only be expected to know when the client itself no longer has a need for an object reference; it cannot be expected to know, and should not know, whether other clients still have live references to an object. To solve this, many second-generation component models use the simple strategy of having every object include a count of the number of clients that have live references to it—this strategy is known as reference counting. Clients notify objects when they store a reference for later use (thus incrementing the reference count) and when they no longer have a need for a previously-held reference (thus decrementing the reference count). The object destroys itself when there are no clients that hold references to it (that is, when the reference count reaches zero). There is thus no need for a destruction counterpart to factories, as objects are responsible for managing their own lifetime.

Reference counting is a form of cooperative garbage collection. It is simple to implement and understand, but has the significant weakness that all clients need to play by the rules at all times. If one client malfunctions, memory leaks or system breakage will ensue.

Counting references has the well-known problem of not being able to handle cyclic references. Consider a trivial example: If an object has a reference to another object, which itself references the first object, these two objects will never be destroyed, regardless of whether there are external parties that reference either; they keep each other alive. (Non-trivial examples normally include a much larger set of objects in the cycle.) This scenario is especially common when one object wishes to subscribe to events from another—the subscribing object obviously must reference the event-providing object in order to subscribe, and when the subscription request has completed, the reverse is also true. Second-generation component models often include means—often error-prone—that seek to make it possible to break such cycles (Szyperski et al. 2002:335).