The failure in the abstraction is actually not the fact that garbage-collection is non-deterministic, but rather in the idea that objects are "interested" in things that they hold references to, and are not interested in things to which they do not hold references. To see why, consider the scenario of an object which maintains a counter of how often a particular control is painted. On creation, it subscribes to the control's "paint" event, and on disposal it unsubscribes. The click event simply increments a field, and a method getTotalClicks()
returns the value of that field.
When the counter object is created, it must cause a reference to itself to be stored within the control it's monitoring. The control really doesn't care about the counter object, and would be just as happy if the counter object, and the reference to it, ceased to exist, but as long as the reference does exist it will call that object's event handler every time it paints itself. This action is totally useless to the control, but would be useful to anyone who would ever call getTotalClicks()
on the object.
If e.g. a method were to create a new "paint-counter" object, perform some action on the control, observe how many times the control was repainted, and then abandon the paint-counter object, the object would remain subscribed to the event even though nobody would ever care if the object and all references to it simply vanished. The objects would not become eligible for collection, however, until the control itself is. If the method were one that would be invoked many thousands of times within the control's lifetime [a plausible scenario], it could cause a memory overflow but for the fact that the cost of N invocations would likely be O(N^2) or O(N^3) unless subscription-processing was very efficient and most operations didn't actually involve any painting.
This particular scenario could be handled by giving having the control keep a weak reference to the counter object rather than a strong one. A weak-subscription model is helpful, but doesn't work in the general case. Suppose that instead of wanting to have an object which monitors a single kind of event from a single control, one wanted to have an event-logger object which monitored several controls, and the system's event-handling mechanism was such that each control needed a reference to a different event-logger object. In that case, the object linking a control to the event logger should remain alive only as long as both the control being monitored and the event logger remain useful. If neither the control nor the event logger holds a strong reference to the linking event, it will cease to exist even though it's still "useful". If either holds a strong event, the lifetime of the linking object may be uselessly extended even if the other one dies.
If no reference to an object exists anywhere in the universe, the object may safely be considered useless and eliminated from existence. The fact that a reference exists to an object, however, does not imply that the object is "useful". In many cases, the actual usefulness of objects will depend upon the existence of references to other objects which--from the GC perspective--are totally unrelated to them.
If objects are deterministically notified when nobody is interested in them, they will be able to use that information to make sure that anyone who would benefit from that knowledge is informed. In the absence of such notification, however, there is no general way to determine what objects are considered "useful" if one knows only the set of references which exist, and not the semantic meaning attached to those references. Thus, any model which assumes that the existence or non-existence of references is sufficient for automated resource management would be doomed even if the GC could instantly detect object abandonment.