Showing posts with label jdk 9. Show all posts
Showing posts with label jdk 9. Show all posts

Friday, November 26, 2021

The final days of finalizers in Java

If you are developing in Java long enough, you are surely aware of the Object::finalize() method and the concept of the finalizers in general.

protected void finalize() throws Throwable
    
...
Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup.
...

This reasonably good idea has gotten quite bad reputation over the years and definitely is one of the worst nightmares of the JVM developers. There are enough details and horror stories on the web related to finalizers (who did not implement it at least once?), but the end is near: JEP-421: Deprecate Finalization for Removal, proposed to become a part of the JDK-18 release, kicks off the process to phase finalizers out.

In this rather short post we are going to talk about java.lang.ref.Cleaner, the mechanism alternative to the finalizers, which allows to perform the cleaning actions once the corresponding object instance becomes phantom reachable. It was introduced by JDK-8138696 and is available starting from JDK-9 and onwards.

The usage of java.lang.ref.Cleaner is pretty straightforward (although advanced usage scenarios are also possible):

To illustrate it, let us consider a quick example. Assume we have been designing a ResourceAccessor class that accesses (or even allocates) some resources (possibly, natives ones, which are outside of the JVM control).

import java.lang.ref.Cleaner;

public class ResourceAccessor implements AutoCloseable {
    private static final Cleaner cleaner = Cleaner.create();
    private final Cleaner.Cleanable cleanable;
    private final Resource resource;

    public ResourceAccessor() {
        this.resource = new Resource();
        this.cleanable = cleaner.register(this, cleaner(resource));
    }

    @Override
    public void close() throws Exception {
        cleanable.clean();
    }

    private static Runnable cleaner(Resource resource) {
        return () -> {
            // Perform cleanup actions
            resource.release();
        };
    }
}

The ResourceAccessor allocates the resource and registers the cleanup action on construction, keeping the reference to Cleaner.Cleanable instance. It also implements AutoCloseable interface and the close() method just delegates to the Cleaner.Cleanable::clean.

Please notice that cleanup action should not hold any references to the object instance registered for cleanup, otherwise that instance will not become phantom reachable and the cleaning action will not be invoked automatically. This is why we have wrapped the lamdba expression behind the cleanup action inside the static method. Alternatively, usage of the standalone class or static nested class that implements Runnable is also possible. Essentially, this is all we need!

Although java.lang.ref.Cleaners provides better and safer alternative to the finalizers, you should not overuse them. The AutoCloseable and try-with-resources idiom should be the preferred approach to manage resources in the majority of situations.

try (final ResourceAccessor resource = new ResourceAccessor()) {
    // Safely use the resource            
}

In these rare conditions when the lifespan of the resource is not well defined, the java.lang.ref.Cleaner is here to help. If you are curious how JDK is using java.lang.ref.Cleaner internally, please take a look at, for example, java.util.Timer or sun.nio.ch.NioSocketImpl classes.

Saturday, May 29, 2021

Chasing Java's release train, from 8 to 16. Part 2: The race to the next LTS release.

In the first part we thoroughly went through the massive amount of features delivered in scope of JDK-9. Nevertheless, this release was always considered as being transitional, with little or no adoption expected. It has a mission to kick off the race towards next LTS release, JDK-11.

JDK 10

JDK-10, the first release followed the six months cadence cycle, brought a number of new features into the language and JVM itself. Let us take a look at the most interesting ones from the developer's perspective.

Undoubtedly, JDK-10 release has quite moderate amount of features comparing to JDK-9, but every one of those was delivered much faster, thanks to the new release cycle.

JDK 11

The first LTS release of the JDK following the new schedule, JDK-11, had seen the light in 2018, six month after JDK-10 release. It finally brought a long awaited stability and established a new baseline in post JDK-9 world. It also included a number of features.

It worth to note that JDK-11 had introduced two new garbage collectors, ZGC and Epsilon, both were marked as experimental. We are going to get back to those in the upcoming posts while discussing more recent JDK releases.

So, where are we today? The JDK-11 slowly but steadily getting more adoption as more and more projects migrate off the JDK-8. Nonetheless, the majority are still on JDK-8 and in my opinion, there are no reasons to expect drastic changes of the balance within next couple of years. But this is another story ...