net.algart.finalizing.Finalizer Maven / Gradle / Ivy
Show all versions of algart Show documentation
/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.algart.finalizing;
import java.lang.ref.*;
import java.util.*;
/**
* 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.{@link #invokeOnDeallocation(Object, Runnable) 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 another 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 {@link #invokeOnDeallocation(Object, Runnable)
* 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 {@link #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.{@link #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.{@link #activeTasksCount()}
* + myFinalizer2.{@link #activeTasksCount()}
* + ...
* + myFinalizerN.{@link #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
*/
public final class Finalizer {
private Thread thread = null;
private int priority = Thread.NORM_PRIORITY;
private final Set taskSet = new HashSet();
private boolean shutdownRequested = false;
private ReferenceQueue