|
|
SpecificationThis model is built on four abstractions: threads, objects, data items, and time. There is a set of threads, which are not further specified here (see UNO Execution Model for details). At any time, this thread pool owns a specific set of data items. For simplicity (but without loss of generality), a fixed, infinite set of objects, O, is assumed. At any time, each object owns a specific set of data items. Data items consist of values of the primitive and structured UNO types (see UNO Type System), and of object references (representing the interface types). An object reference is either the null reference or a reference to any object o ∈ O. At any time t, TReft ⊆ O is the set of objects directly referenced from the thread pool. These are all the objects referenced from the data items owned by the thread pool at time t. At any time t, for each object o ∈ O, reft(o) ⊆ O is the set of objects directly referenced from object o. These are all the objects referenced from the data items owned by object o at time t. The function ref+t is the transitive hull of the function reft; at any time t, ref+t(o) represents the set of objects referenced from object o. Viewed as a relation, reft describes a directed graph with objects as vertices and object references as edges. An object reference circle is a strongly connected component of that graph, with the restriction that it must contain at least one edge. (Informally, an object reference circle is a maximal set of objects where each object can be reached from every other object via one or more object references.) At any time t, TRef+t := TReft ∪ ∪o ∈ TReft ref+t(o) is the set of objects referenced from the thread pool. At any time t, O can be partitioned into four disjoint subsets: the immaterial objects Immt, the active objects Actt, the done objects Dont, and the unreachable objects Unrt. Two of these are derived as follows: Actt := TRef+t, and Unrt := O \ (Immt ∪ Actt ∪ Dont). Initial state:
Transitions:
ExplanationOver time, each individual object can transition from Imm to Act, and then to either Don or Unr. Each object starts out as immaterial. The set of data items owned by an immaterial object (and hence its set of directly referenced objects) is empty. An object becomes active once it has been created, and stays active as long as it is referenced from the thread pool. The set of data items owned by an object (and hence its set of directly referenced objects) can only change while the object is active. An object becomes done once neither it is referenced from the thread pool, nor is it a member of an object reference circle. An object becomes unreachable once it is no longer referenced from the thread pool, but is a member of an object reference circle. Unreachable objects are a problem, as they can cause resource leaks. A desired strategy is to keep Unrt empty at all times. Concepts from different language bindings map to the model's abstractions in different ways. Two prototypical languages are C++ (providing constructors and destructors of objects, but no garbage collection) and Java (providing constructors and finalizers of objects, with automatic garbage collection). To implement the UNO object life cycle model, the C++ language binding uses reference counting for both internal and external (bridged) objects. The Java language binding relies on garbage collection for internal objects, and uses garbage collection together with reference counting for external (bridged) objects. For C++, the relation is as follows. Calling the constructor of an object coincides with the object's transition from immaterial to active. After an object's transition from active to done, the destructor of that object will eventually be called. The destructor is called immediately if the object is only referenced locally, but it can be delayed arbitrarily if the object is referenced externally (over a bridge). Unreachable objects cause memory leaks, as their destructors are never called. For Java, the relation is slightly different. Again, calling the constructor of an object coincides with the object's transition from immaterial to active. After an object's transition from active to done, the finalizer of that object will eventually be called. For an unreachable object, the finalizer will eventually be called if the object is only referenced locally; the finalizer will not be called (and the object will cause a memory leak) if the object is referenced externally (over a bridge). This has two implications:
ApplicationThere are various strategies how to avoid or break object reference circles, and how to make objects release resources in a timely fashion. Object OwnershipOne strategy to cope with object reference circles is to allow them, but to ensure that they are broken before the involved objects become unreachable. Lets assume that {o1, …, on}, n ≥ 1, form an object reference circle. Exactly one of the objects in the circle is required to have a so-called owner o′ ∉ {o1, …, on}; assume that o′ is the owner of o1. As long as there are any references to the circle (from objects outside the circle, or from the thread pool), it is required that o′ has a reference to o1. Whenever the reference from the owner o′ to o1 is the only outside reference to the circle, there is a choice. Either, the owner decides to keep the circle active, so that other outside references to the circle can be made in the future. Or, the owner decides to be done with the circle. In that case, o′ must ensure that the circle does not become unreachable when it cuts its last outside reference to o1. The easiest solution is that o′ tells o1 to break the circle, by clearing all references from o1 to any of {o1, …, on}. Note that this only works if the circle is sufficiently simple, i.e., if removing the references from o1 does not introduce any new (smaller) object reference circles. There are two difficulties with this approach:
ComponentsThe approach of First, the owned object o1 (implementing
That way, the owner does not need to notice exactly when it has the only outside reference to the circle. Of course, users of o1 now have to cope with the disposed state. Generally, it should still be ensured that there is as little access as possible to the object after it has been disposed. Second, the Weak ReferencesThe combination of It has to be further investigated how weak references fit in with the UNO
object life cycle model, and the object ownership and DisposingAs explained, a UNO object that has acquired any references should release them well before the language binding has determined that the object is no longer active (e.g., via a destructor or finalizer call), as there can be an arbitrarily long time span between the object becoming done or unreachable, and the destructor or finalizer being called. The UNO object should offer an explicit mechanism to release its resources, probably as a method of a supported interface. This is similar to the object ownership/
|



