Finalizers are unpredictable, often dangerous, and generally unnecessary.
As of Java 9, finalizers have been deprecated, but they are still being used by the Java libraries. The Java 9 replacement for finalizers is cleaners. Cleaners are less dangerous than finalizers, but still unpredictable, slow, and generally unnecessary.
The
In Java, the garbage collector reclaims the storage associated with an object when it becomes unreachable, requiring no special effort on the part of the programmer.
C++ destructors are also used to reclaim other nonmemory resources. In Java, a
This means that you should never do anything time-critical in a finalizer or cleaner. For example, it is a grave error to depend on a finalizer or cleaner to close files because open file descriptors are a limited resource.
Don’t be seduced by the methods
There is a severe performance penalty for using finalizers and cleaners. On my machine, the time to create a simple
Finalizers have a serious security problem: they open your class up to finalizer attacks.
The idea behind a finalizer attack is simple: If an exception is thrown from a constructor or its serialization equivalents—the
Throwing an exception from a constructor should be sufficient to prevent an object from coming into existence; in the presence of finalizers, it is not. Such attacks can have dire consequences. Final classes are immune to finalizer attacks because no one can write a malicious subclass of a final class. To protect nonfinal classes from finalizer attacks, write a final
So what should you do instead of writing a finalizer or cleaner for a class whose objects encapsulate resources that require termination, such as files or threads? Just have your class implement
if anything, are cleaners and finalizers good for?
One is to act as a safety net in case the owner of a resource neglects to call its
A second legitimate use of cleaners concerns objects with native peers. A native peer is a native (non-Java) object to which a normal object delegates via native methods.
// An autocloseable class using a cleaner as a safety net
public class Room implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();
// Resource that requires cleaning. Must not refer to Room!
private static class State implements Runnable {
int numJunkPiles; // Number of junk piles in this room
State(int numJunkPiles) {
this.numJunkPiles = numJunkPiles;
}
// Invoked by close method or cleaner
@Override public void run() {
As of Java 9, finalizers have been deprecated, but they are still being used by the Java libraries. The Java 9 replacement for finalizers is cleaners. Cleaners are less dangerous than finalizers, but still unpredictable, slow, and generally unnecessary.
The
Cleaner
and PhantomReference
provide more flexible and efficient ways to release resources when an object becomes unreachable.In Java, the garbage collector reclaims the storage associated with an object when it becomes unreachable, requiring no special effort on the part of the programmer.
C++ destructors are also used to reclaim other nonmemory resources. In Java, a
try
-with-resources or try
-finally
block is used for this purpose (Item 9).This means that you should never do anything time-critical in a finalizer or cleaner. For example, it is a grave error to depend on a finalizer or cleaner to close files because open file descriptors are a limited resource.
Don’t be seduced by the methods
System.gc
and System.runFinalization
. They may increase the odds of finalizers or cleaners getting executed, but they don’t guarantee it. Two methods once claimed to make this guarantee: System.runFinalizersOnExit
and its evil twin, Runtime.runFinalizersOnExit
. These methods are fatally flawed and have been deprecated for decades [ThreadStop].There is a severe performance penalty for using finalizers and cleaners. On my machine, the time to create a simple
AutoCloseable
object, to close it using try
-with-resources, and to have the garbage collector reclaim it is about 12 ns. Using a finalizer instead increases the time to 550 ns. In other words, it is about 50 times slower to create and destroy objects with finalizers. Cleaners are comparable in speed to finalizers .cleaning, and destroying an object takes about 66 ns on my machine, Finalizers have a serious security problem: they open your class up to finalizer attacks.
The idea behind a finalizer attack is simple: If an exception is thrown from a constructor or its serialization equivalents—the
readObject
and readResolve
methods (Chapter 12)—the finalizer of a malicious subclass can run on the partially constructed object that should have “died on the vine.”Throwing an exception from a constructor should be sufficient to prevent an object from coming into existence; in the presence of finalizers, it is not. Such attacks can have dire consequences. Final classes are immune to finalizer attacks because no one can write a malicious subclass of a final class. To protect nonfinal classes from finalizer attacks, write a final
finalize
method that does nothing.So what should you do instead of writing a finalizer or cleaner for a class whose objects encapsulate resources that require termination, such as files or threads? Just have your class implement
AutoCloseable
, and require its clients to invoke the close
method on each instance when it is no longer needed, typically using try
-with-resources to ensure termination even in the face of exceptions (Item 9). One detail worth mentioning is that the instance must keep track of whether it has been closed: the close
method must record in a field that the object is no longer valid, and other methods must check this field and throw an IllegalStateException
if they are called after the object has been closed.if anything, are cleaners and finalizers good for?
One is to act as a safety net in case the owner of a resource neglects to call its
close
method.A second legitimate use of cleaners concerns objects with native peers. A native peer is a native (non-Java) object to which a normal object delegates via native methods.
// An autocloseable class using a cleaner as a safety net
public class Room implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();
// Resource that requires cleaning. Must not refer to Room!
private static class State implements Runnable {
int numJunkPiles; // Number of junk piles in this room
State(int numJunkPiles) {
this.numJunkPiles = numJunkPiles;
}
// Invoked by close method or cleaner
@Override public void run() {
System.out.println("Cleaning room");
numJunkPiles = 0;
}
}
// The state of this room, shared with our cleanable
private final State state;
// Our cleanable. Cleans the room when it’s eligible for gc
private final Cleaner.Cleanable cleanable;
public Room(int numJunkPiles) {
state = new State(numJunkPiles);
cleanable = cleaner.register(this, state);
}
@Override public void close() {
cleanable.clean();
}
}
numJunkPiles = 0;
}
}
// The state of this room, shared with our cleanable
private final State state;
// Our cleanable. Cleans the room when it’s eligible for gc
private final Cleaner.Cleanable cleanable;
public Room(int numJunkPiles) {
state = new State(numJunkPiles);
cleanable = cleaner.register(this, state);
}
@Override public void close() {
cleanable.clean();
}
}
public class Adult {
public static void main(String[] args) {
try (Room myRoom = new Room(7)) {
System.out.println("Goodbye");
}
}
}
public static void main(String[] args) {
try (Room myRoom = new Room(7)) {
System.out.println("Goodbye");
}
}
}
As you’d expect, running the
Adult
program prints Goodbye
, followed by Cleaning room
. But what about this ill-behaved program, which never cleans its room?
public class Teenager {
public static void main(String[] args) {
new Room(99);
System.out.println("Peace out");
}
}
public static void main(String[] args) {
new Room(99);
System.out.println("Peace out");
}
}
You might expect it to print
Peace out
, followed by Cleaning room
, but on my machine, it never prints Cleaning room
; it just exits.
In summary, don’t use cleaners, or in releases prior to Java 9, finalizers, except as a safety net or to terminate noncritical native resources. Even then, beware the indeterminacy and performance consequences.
No comments:
Post a Comment