Objects with an enabled finalizer are ineligible for collection; the GC only examines them after determining all the other objects which are ineligible for collection, however, and makes note of all the objects which would have been eligible for collection but for the existence of the enabled finalizer, and runs the finalize
methods of such objects as soon as practical. Finalizable objects won't become eligible for collection until the finalizer has run, but the GC will have no way of distinguishing objects which become eligible for finalization as soon as the finalizer finishes, or those which were rendered ineligible for finalization as a result of actions by some object's finalizer and became eligible for collection at some later time.
The .NET Framework includes methods called IIRC GC.SuppressFinalize
and GC.ReRegisterForFinalization
which make it possible for code which knows an object's finalizer won't do anything useful to tell the GC not to bother calling it, and allows for code which knows a finalizer ran "too soon" to request that it run again later. The JVM, however, does not include such a feature. Since having all finalizable objects automatically reregistered for finalization once the finalizer runs would prevent them from ever getting collected, and since there's no way to manually reregister them, the net consequence is that there's no usable pattern via which an object's finalizer can be run more than once.
On the other hand, it is possible to achieve a similar effect by defining a nested class object which is finalizable, having the outer class object hold a reference to a nested-class instance, and having that nested class instance's "finalize" method chain back to cleanup code in its owner. If that cleanup code discards the nested-class instance and replaces it with a new one, then that new instance will trigger its finalizer (chaining back to its owner) on the next GC cycle where the owner is found to be unreferenced.