All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.algart.finalizing.Finalizer Maven / Gradle / Ivy

Go to download

Open-source Java libraries, supporting generalized smart arrays and matrices with elements of any types, including a wide set of 2D-, 3D- and multidimensional image processing and other algorithms, working with arrays and matrices.

There is a newer version: 1.4.23
Show newest version
/*
 * 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.

* *
    *
  1. Declaration of finalize() method slows down instantiation of objects. * Finalization via Finalizer class does not lead to any slowing.
  2. * *
  3. Finalizer class allows to control the thread, which performs finalization tasks, * in particular, to stop it or to set its priority.
  4. * *
  5. Finalizer class allows to do something on deallocation of any object, * even if we have no ability to override its methods.
  6. *
* *

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 refQueue = null; /** * 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. */ public Finalizer() { } /** * Schedules running of the given task (its run() method) * at the moment when the checkedForDeallocation object will become unreachable * (more precisely, phantom 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 another 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. * * @param checkedForDeallocation some object. * @param task a task thah will be performed on deallocation * of checkedForDeallocation object. */ public void invokeOnDeallocation(Object checkedForDeallocation, Runnable task) { synchronized (taskSet) { if (thread == null) { refQueue = new ReferenceQueue(); shutdownRequested = false; thread = new CleanupThread(this); thread.setDaemon(true); try { thread.setPriority(priority); } catch (SecurityException ignored) { } thread.start(); } new PhantomFinalizeHolder(this, checkedForDeallocation, task); } } /** * Returns the current number of tasks that are scheduled by * {@link #invokeOnDeallocation(Object, Runnable) invokeOnDeallocation} method, * but not fully performed yet. * * @return the current number of scheduled tasks. */ public int activeTasksCount() { synchronized (taskSet) { return taskSet.size(); } } /** * 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. */ public void shutdownNow() { synchronized (taskSet) { if (thread != null) { shutdownRequested = true; thread.interrupt(); } } } /** * 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. * * @param priority priority to set the internal thread to. * @throws IllegalArgumentException if the priority is not in the * range Thread.MIN_PRIORITY..Thread.MAX_PRIORITY. */ public void setPriority(int priority) { synchronized (taskSet) { if (priority > Thread.MAX_PRIORITY || priority < Thread.MIN_PRIORITY) { throw new IllegalArgumentException(); } this.priority = priority; if (thread != null) { try { thread.setPriority(priority); } catch (SecurityException ignored) { } } } } private static class PhantomFinalizeHolder extends PhantomReference { final Runnable task; PhantomFinalizeHolder(Finalizer fin, Object checkedForDeallocation, Runnable task) { super(checkedForDeallocation, fin.refQueue); this.task = task; fin.taskSet.add(this); // avoid deallocation of this reference before the cleanup procedure } } private static class CleanupThread extends Thread { final Finalizer fin; CleanupThread(Finalizer fin) { this.fin = fin; } public void run() { while (true) { PhantomFinalizeHolder phantomHolder = null; Reference holder = null; try { holder = fin.refQueue.remove(); phantomHolder = (PhantomFinalizeHolder) holder; } catch (InterruptedException ignored) { } if (phantomHolder != null) { phantomHolder.task.run(); // run() method may try to control the current thread, // in particular, to clear "interrupted" flag } if (holder != null) { synchronized (fin.taskSet) { fin.taskSet.remove(holder); } } synchronized (fin.taskSet) { if (fin.shutdownRequested) { fin.taskSet.clear(); fin.thread = null; return; } } }//while } } }