diff --git a/content/io/file-memory-mapping.yaml b/content/io/file-memory-mapping.yaml index d8b7a2e..9d0f0b9 100644 --- a/content/io/file-memory-mapping.yaml +++ b/content/io/file-memory-mapping.yaml @@ -52,7 +52,7 @@ whyModernWins: support: state: "available" description: "Available since JDK 22 (March 2024)" -prev: "io/try-with-resources-effectively-final" +prev: "io/finalizers-to-resource-cleanup" next: "io/files-mismatch" related: - "io/http-client" diff --git a/content/io/finalizers-to-resource-cleanup.yaml b/content/io/finalizers-to-resource-cleanup.yaml new file mode 100644 index 0000000..5ace66a --- /dev/null +++ b/content/io/finalizers-to-resource-cleanup.yaml @@ -0,0 +1,79 @@ +--- +id: 45488112-44bc-4a75-a6c6-a69849147ded +slug: "finalizers-to-resource-cleanup" +title: "Finalizers to deterministic resource cleanup" +category: "io" +difficulty: "advanced" +jdkVersion: "9" +oldLabel: "Finalization" +modernLabel: "Java 9+" +oldApproach: "Override finalize()" +modernApproach: "AutoCloseable and Cleaner" +oldCode: |- + @Override + protected void finalize() throws Throwable { + nativeHandle.release(); + } +modernCode: |- + final class NativeResource implements AutoCloseable { + private static final Cleaner CLEANER = Cleaner.create(); + + private static final class State implements Runnable { + private final long handle; + + State(long handle) { + this.handle = handle; + } + + @Override + public void run() { + release(handle); + } + } + + private final Cleaner.Cleanable cleanable; + + NativeResource(long handle) { + cleanable = CLEANER.register(this, new State(handle)); + } + + @Override + public void close() { + cleanable.clean(); + } + } +summary: "Replace finalization with AutoCloseable and use Cleaner only as a defensive fallback." +explanation: "Finalization is deprecated for removal and provides neither prompt cleanup\ + \ nor reliable ordering. Implement AutoCloseable so callers can release resources deterministically\ + \ with try-with-resources. For leaked resources, Cleaner can invoke the same idempotent\ + \ cleanup action as a last resort; its action must not retain the registered object." +whyModernWins: +- icon: "⏱" + title: "Deterministic" + desc: "Try-with-resources releases resources at a known point." +- icon: "🛡" + title: "Safer lifecycle" + desc: "Avoids finalizer resurrection and unpredictable ordering." +- icon: "🧹" + title: "Defensive fallback" + desc: "Cleaner can recover leaked resources without overriding finalize()." +support: + state: "available" + description: "Cleaner and finalization deprecation available since JDK 9 (September 2017)" +prev: "io/try-with-resources-effectively-final" +next: "io/file-memory-mapping" +related: +- "io/try-with-resources-effectively-final" +- "io/file-memory-mapping" +- "concurrency/executor-try-with-resources" +tags: +- io +- native +- migration +docs: +- title: "Cleaner" + href: "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ref/Cleaner.html" +- title: "AutoCloseable" + href: "https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/AutoCloseable.html" +- title: "JEP 421: Deprecate Finalization for Removal" + href: "https://openjdk.org/jeps/421" diff --git a/content/io/try-with-resources-effectively-final.yaml b/content/io/try-with-resources-effectively-final.yaml index 1e8a49b..5fd862e 100644 --- a/content/io/try-with-resources-effectively-final.yaml +++ b/content/io/try-with-resources-effectively-final.yaml @@ -39,7 +39,7 @@ support: state: "available" description: "Widely available since JDK 9 (Sept 2017)" prev: "io/path-of" -next: "io/file-memory-mapping" +next: "io/finalizers-to-resource-cleanup" related: - "io/path-of" - "io/reading-files" diff --git a/proof/io/FinalizersToResourceCleanup.java b/proof/io/FinalizersToResourceCleanup.java new file mode 100644 index 0000000..9f78193 --- /dev/null +++ b/proof/io/FinalizersToResourceCleanup.java @@ -0,0 +1,44 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//JAVA 25+ + +import java.lang.ref.Cleaner; + +/// Proof: finalizers-to-resource-cleanup +/// Source: content/io/finalizers-to-resource-cleanup.yaml +final class NativeResource implements AutoCloseable { + private static final Cleaner CLEANER = Cleaner.create(); + + private static final class State implements Runnable { + private final long handle; + + State(long handle) { + this.handle = handle; + } + + @Override + public void run() { + release(handle); + } + } + + private final Cleaner.Cleanable cleanable; + + NativeResource(long handle) { + cleanable = CLEANER.register(this, new State(handle)); + } + + private static void release(long handle) { + IO.println("Released native handle " + handle); + } + + @Override + public void close() { + cleanable.clean(); + } +} + +void main() { + try (var resource = new NativeResource(42L)) { + IO.println("Using native resource"); + } +}