Class Finalizer
Finalizer: an universal tool allowing to perform any task on deallocation of some object.
This class can be a helpful alternative to the standard finalize() method. It is based on the mechanism of phantom references and may be better solution than finalize() method in many situations.
The main advantages of Finalizer class are the following.
- Declaration of finalize() method slows down instantiation of objects. Finalization via Finalizer class does not lead to any slowing.
- Finalizer class allows to control the thread, which performs finalization tasks, in particular, to stop it or to set its priority.
- Finalizer class allows to do something on deallocation of any object, even if we have no ability to override its methods.
The typical example of using this class is the following:
class MyClass {
static final Finalizer fin = new Finalizer();
. . .
void myMethod() {
// . . .
// some Java code
// . . .
final SomeType largeResources = ...; // some external resources associated with "data" object
fin.invokeOnDeallocation
(data, new Runnable() {
public void run() {
try {
... // disposing largeResources
} catch (Throwable ex) {
ex.printStackTrace(); // or logging the exception by some logger
}
}
}
// . . .
// some Java code
// . . .
}
});
In this example, run() method will be performed at the moment when data object will be ready for deallocation by garbage collector.
Important note: the implementation of Runnable interface must not contain any direct or indirect references to data object. In other case, the data instance will never become unreachable and the run() method will never be called. In particular, largeResources object, processed by run() method and accessible there via "final" declaration, must not refer to data instance in any ways. In other words, unlike the standard finalize() method, the finalization code, scheduled by this class, cannot refer to the finalized data.
Every Finalizer instance contains a daemon thread, that is started on the first
call of invokeOnDeallocation(checkedForDeallocation,task)
method.
This thread looks, in an infinite loop, for deallocation of all objects,
passed to that method, and runs corresponding tasks when the objects
("checked for deallocation") become unreachable.
This thread will run all time until closing the application,
if you will not stop it by shutdownNow()
method.
So, you should avoid creating extra instances of Finalizer:
please use one or several global finalizers for a package or application.
As well as for the classic finalize() method, there is no guarantee that finalization tasks scheduled by this class will be really performed before finishing the application. Usually, exiting the application just stops all daemon threads inside all instances of this class, and the tasks, which were not completed yet, are canceled.
To increase probability of performing all finalization tasks, you may add a code alike the following at the point when application is finished (or closed by a user):
long t = System.currentTimeMillis();
while (myFinalizer.activeTasksCount()
> 0) {
System.runFinalization();
System.gc();
Thread.sleep(50);
if (System.currentTimeMillis() - t > TIMEOUT_IN_MILLISECONDS)
break;
}
This "while" loop here waits until all tasks, scheduled in myFinalizer, will be successfully finished. The loop should be restricted by some suitable, not too long timeout:
- firstly, because System.runFinalization() and System.gc() do not guarantee finalization of any object,
- secondly, because the activeTasksCount value will never become zero, if some references to data instances, scheduled for finalization, were not "forgotten" (have not become unreachable) due to some bug in the other application code.
If your system use several finalizers, you should perform the same loop for each one, or replace the single call of activeTasksCount() with the sum of results of this method for all finalizers:
... while (myFinalizer1.activeTasksCount()
+ myFinalizer2.activeTasksCount()
+ ... + myFinalizerN.activeTasksCount()
> 0) { ...
It's possible that your task object contains references to some other "large" objects, which also should be finalized before finishing your application and which implement finalization in another way (for example, some standard Java objects as MappedByteBuffer). When the "while" loop, listed above, finishes, such objects become unreachable, but not really finalized yet. To be on the safe side, we recommend to add the following loop after the "while" loop listed above:
for (int k = 0; k < 5; k++) { // finalizing some additional objects that could be // referred from finalization tasks performed above System.runFinalization(); System.gc(); }
This class is thread-safe: you may use the same instance of this class in several threads.
- Author:
- Daniel Alievsky
-
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionint
Returns the current number of tasks that are scheduled byinvokeOnDeallocation
method, but not fully performed yet.void
invokeOnDeallocation
(Object checkedForDeallocation, Runnable task) Schedules running of the given task (its run() method) at the moment when the checkedForDeallocation object will become unreachable (more precisely, phantomly reachable).void
setPriority
(int priority) Sets the priority of the thread serving this finalizer.void
Shutdown the finalizer.
-
Constructor Details
-
Finalizer
public Finalizer()Creates new instance of finalizer.Please avoid creating extra finalizers. It is a good practice to create only one finalizer for a class or package requiring finalization tehcnique, for example, in a package-private static field.
-
-
Method Details
-
invokeOnDeallocation
Schedules running of the given task (its run() method) at the moment when the checkedForDeallocation object will become unreachable (more precisely, phantomly reachable).The first call of this method starts the internal thread in this object. This thread will look, in an infinite loop, for the levels of reachability of all objects passed to this method as checkedForDeallocation argument, and will call corresponding tasks when these objects will become phantomly reachable.
Important: the implementation of task must not contain references to the passed checkedForDeallocation instance! In other case, this instance will never become unreachable and task.run() method will never be called.
Note: if the class of checkedForDeallocation object, or the class of the last object which allows to reach checkedForDeallocation, declares standard finalize() method, then the task may not be called while the first System.gc() call after the moment when checkedForDeallocation object will become phantomly reachable, but only while second or further System.gc() calls.
- Parameters:
checkedForDeallocation
- some object.task
- a task thah will be performed on deallocation of checkedForDeallocation object.
-
activeTasksCount
public int activeTasksCount()Returns the current number of tasks that are scheduled byinvokeOnDeallocation
method, but not fully performed yet.- Returns:
- the current number of scheduled tasks.
-
shutdownNow
public void shutdownNow()Shutdown the finalizer. If some task is running now, it will be completed. All tasks that are not running yet will be removed and will not be performed.You should not use this Finalizer instance after calling this method.
You may call this method if you are absolutely sure that this finalizer will not be useful more. It is the only way to stop the thread serving this finalizer before finishing the application.
-
setPriority
public void setPriority(int priority) Sets the priority of the thread serving this finalizer. The argument of this method will be passed to standard Thread.setPriority method.Unlike Thread.setPriority method, the SecurityException (that can occur while setting priority) will be ignored: if it occurs, the priority is not changed.
- Parameters:
priority
- priority to set the internal thread to.- Throws:
IllegalArgumentException
- if the priority is not in the range Thread.MIN_PRIORITY..Thread.MAX_PRIORITY.
-